diff --git a/.env.example b/.env.example index e56c3da83..f8ab37bcd 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,4 @@ PRIVATE_KEY_FILE= PUBLIC_KEY_FILE= SENTRY_DSN= MCO_LOG_LEVEL= +MCO_LOGGING_GROUPS= diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index fe3f1fbf4..34037d37a 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -5,7 +5,6 @@ on: pull_request: workflow_dispatch: merge_group: - jobs: build-test: @@ -13,7 +12,7 @@ jobs: strategy: matrix: - node-version: [20.x, 22.x, 23.x] + node-version: [22.x, 23.x, 24.x] steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index cebe3166d..4fa23b6ff 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,10 +1,11 @@ { "recommendations": [ - "ms-azuretools.vscode-docker", "ms-vscode.makefile-tools", "usernamehw.errorlens", - "biomejs.biome", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" + "ms-azuretools.vscode-containers", + "kisstkondoros.vscode-codemetrics", + "mhutchie.git-graph", + "vitest.explorer", + "biomejs.biome" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index e7f2ca974..bb6257fc1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,23 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Typescript", + "type": "node", + "runtimeExecutable": "pnpx", + "runtimeVersion": "23.5.0", + "request": "launch", + "args": ["src/server.ts"], + "runtimeArgs": [ + "tsx", + "--import", + "./instrument.mjs", + "--openssl-legacy-provider" + ], + "cwd": "${workspaceRoot}", + "envFile": "${workspaceFolder}/.env", + "internalConsoleOptions": "openOnSessionStart" + }, { "name": "Stop Prod", "request": "launch", diff --git a/.vscode/settings.json b/.vscode/settings.json index ea51765b1..c7671aeb6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { - "git.enableCommitSigning": true, - "typescript.tsdk": "node_modules/typescript/lib", - "js/ts.implicitProjectConfig.checkJs": true + "git.enableCommitSigning": true, + "typescript.tsdk": "node_modules/typescript/lib", + "js/ts.implicitProjectConfig.checkJs": true, + "vitest.rootConfig": "vite.config.ts" } diff --git a/Makefile b/Makefile index 3ef59d27b..f90bc7f1f 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,7 @@ docker-init: ## Start the project in docker .PHONY: clean clean: ## Clean the project + @rm -rf node_modules -v @rm -rf */**/node_modules -v @rm -rf dist -v diff --git a/README.md b/README.md index b4690af3e..9ad8369cd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # mcos -[![Node.js CI](https://github.com/drazisil/mcos/actions/workflows/node.yml/badge.svg?branch=dev)](https://github.com/drazisil/mcos/actions/workflows/node.yml) [![CodeQL](https://github.com/drazisil/mcos/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/drazisil/mcos/actions/workflows/codeql-analysis.yml?branch=dev) [![codecov](https://codecov.io/gh/rustymotors/server/graph/badge.svg?token=XiwYgbHCeN)](https://codecov.io/gh/rustymotors/server) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) +[![Node.js CI](https://github.com/drazisil/mcos/actions/workflows/node.yml/badge.svg?branch=dev)](https://github.com/drazisil/mcos/actions/workflows/node.yml) [![CodeQL](https://github.com/drazisil/mcos/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/drazisil/mcos/actions/workflows/codeql-analysis.yml?branch=dev) [![codecov](https://codecov.io/gh/rustymotors/server/graph/badge.svg?token=XiwYgbHCeN)](https://codecov.io/gh/rustymotors/server) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/rustymotors/server?utm_source=oss&utm_medium=github&utm_campaign=rustymotors%2Fserver&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) ## About @@ -18,20 +18,21 @@ There's a brief explanation of the thought process here [link](https://github.co ## Server Setup -- See [server docs](./docs/server.md) +- See [server docs](./docs/server.md) +- See [configuration docs](./config/README.md) ## Client Setup -- See [client docs](./docs/client.md) +- See [client docs](./docs/client.md) ## Timeline -- March 6, 2016 - Started +- March 6, 2016 - Started -- October 12, 2023 - Connected to lobby +- October 12, 2023 - Connected to lobby ![img The first non-hacked image of the MCO lobby since it was shutdown](images/2012-10-12_lobby.png) ## Current Status -- [TODO Issues](https://github.com/drazisil/mcos/labels/todo%20%3Aspiral_notepad%3A) +- [TODO Issues](https://github.com/drazisil/mcos/labels/todo%20%3Aspiral_notepad%3A) diff --git a/config/README.md b/config/README.md new file mode 100644 index 000000000..10887d691 --- /dev/null +++ b/config/README.md @@ -0,0 +1,82 @@ +# Configuration System for Rusty Motors Server + +This project uses the [`config`](https://www.npmjs.com/package/config) and [`dotenv`](https://www.npmjs.com/package/dotenv) packages for secure, flexible, and environment-based configuration management. + +## How It Works + +- **Config Directory:** All configuration files are located in the root-level `config/` directory. +- **Default Values:** The main configuration is in `config/default.json`. +- **Environment Variables:** You can override any config value using environment variables, as mapped in `config/custom-environment-variables.json`. +- **.env Support:** You can also use a `.env` file in the project root to set environment variables for local development. +- **Environment-Specific Config:** You can add files like `config/production.json` or `config/development.json` for environment-specific overrides. + +## How to Use + +1. **Edit `config/default.json`** to set project-wide defaults (see example below). +2. **Override with environment variables** (e.g., in your shell, CI/CD, or `.env` file): + - Example: `export CERTIFICATE_FILE=/path/to/cert.pem` +3. **(Optional) Add a `.env` file** in the project root for local development: + ```env + CERTIFICATE_FILE=./data/mcouniverse.crt + PRIVATE_KEY_FILE=./data/private_key.pem + PUBLIC_KEY_FILE=./data/pub.key + EXTERNAL_HOST=localhost + MCO_LOG_LEVEL=debug + ``` +4. **(Optional) Add `production.json`, `test.json`, etc.** for environment-specific config. + +## Example: `config/default.json` + +```json +{ + "host": "localhost", + "logLevel": "debug", + "certificateFile": "./data/mcouniverse.crt", + "privateKeyFile": "./data/private_key.pem", + "publicKeyFile": "./data/pub.key" +} +``` + +## Example: `config/custom-environment-variables.json` + +```json +{ + "host": "EXTERNAL_HOST", + "logLevel": "MCO_LOG_LEVEL", + "certificateFile": "CERTIFICATE_FILE", + "privateKeyFile": "PRIVATE_KEY_FILE", + "publicKeyFile": "PUBLIC_KEY_FILE" +} +``` + +## How the Code Loads Config + +The code uses the `config` package to load settings. For example: + +```typescript +import config from 'config'; +const host = config.get('host'); +``` + +Or, using the provided helper: + +```typescript +import { getServerConfiguration } from 'rusty-motors-shared'; +const config = getServerConfiguration(); +console.log(config.host); +``` + +## Best Practices + +- **Never commit secrets** (like private keys) directly to config files. Use environment variables or a secure secrets manager. +- **Document any required config values** for new developers. +- **Check the config/ directory** for all available options and mappings. + +## Further Reading + +- [node-config documentation](https://github.com/node-config/node-config/wiki/Configuration-Files) +- [dotenv documentation](https://github.com/motdotla/dotenv) + +--- + +For questions, see the project README or ask in the project chat. diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json new file mode 100644 index 000000000..5d2dbd98c --- /dev/null +++ b/config/custom-environment-variables.json @@ -0,0 +1,7 @@ +{ + "host": "EXTERNAL_HOST", + "logLevel": "MCO_LOG_LEVEL", + "certificateFile": "CERTIFICATE_FILE", + "privateKeyFile": "PRIVATE_KEY_FILE", + "publicKeyFile": "PUBLIC_KEY_FILE" +} diff --git a/config/default.json b/config/default.json new file mode 100644 index 000000000..7d387f748 --- /dev/null +++ b/config/default.json @@ -0,0 +1,7 @@ +{ + "host": "localhost", + "logLevel": "debug", + "certificateFile": "./data/mcouniverse.crt", + "privateKeyFile": "./data/private_key.pem", + "publicKeyFile": "./data/pub.key" +} diff --git a/instrument.mjs b/instrument.mjs index 5b86a5ca1..e13fa43ff 100644 --- a/instrument.mjs +++ b/instrument.mjs @@ -2,16 +2,14 @@ import * as Sentry from '@sentry/node'; import { nodeProfilingIntegration } from '@sentry/profiling-node'; Sentry.init({ - dsn: process.env['SENTRY_DSN'], - integrations: [ - // Add our Profiling integration - nodeProfilingIntegration(), - ], - - // We recommend adjusting this value in production, or using tracesSampler - // for finer control - tracesSampleRate: 1.0, - profilesSampleRate: 1.0, // Profiling sample rate is relative to tracesSampleRate + dsn: process.env['SENTRY_DSN'], + integrations: [ + // Add our Profiling integration + nodeProfilingIntegration(), + ], + + // We recommend adjusting this value in production, or using tracesSampler + // for finer control + tracesSampleRate: 1.0, + profilesSampleRate: 1.0, // Profiling sample rate is relative to tracesSampleRate }); - - diff --git a/libs/@rustymotors/binary/src/index.ts b/libs/@rustymotors/binary/src/index.ts index 6bd67cd7c..1dc276b67 100644 --- a/libs/@rustymotors/binary/src/index.ts +++ b/libs/@rustymotors/binary/src/index.ts @@ -1,5 +1,21 @@ -export * from "./lib/Bytable"; -export * from "./lib/BytableContainer"; -export * from "./lib/BytableDword"; -export * from "./lib/BytableHeader"; -export * from "./lib/BytableMessage"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export * from './lib/Bytable.js'; +export * from './lib/BytableContainer.js'; +export * from './lib/BytableDword.js'; +export * from './lib/BytableHeader.js'; +export * from './lib/BytableMessage.js'; diff --git a/libs/@rustymotors/binary/src/lib/Bytable.ts b/libs/@rustymotors/binary/src/lib/Bytable.ts index 2373ac027..165ef873b 100644 --- a/libs/@rustymotors/binary/src/lib/Bytable.ts +++ b/libs/@rustymotors/binary/src/lib/Bytable.ts @@ -1,71 +1,94 @@ -import { get } from "http"; -import { BytableBase } from "./BytableBase"; -import { BytableObject } from "./types"; -import { getServerLogger } from "rusty-motors-shared"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . -export class Bytable extends BytableBase implements BytableObject { - protected name_: string = ""; - protected value_: string | number | Buffer = ""; +import { BytableBase } from './BytableBase.js'; +import { BytableObject } from './types.js'; - static fromBuffer(buffer: Buffer, offset: number) { - const bytable = new this(); +export class Bytable extends BytableBase implements BytableObject { + protected name_ = ''; + protected value_: string | number | Buffer = ''; - if (buffer.length === 4 && offset === 4) { - // Some messages only consist of a id and a length - getServerLogger().warn(`Buffer length is 4, skipping deserialization`); - return bytable; - } - - if (!buffer || offset < 0 || offset >= buffer.length) { - getServerLogger().error(`Cannot deserialize buffer with invalid offset: ${offset}`); - return bytable; - } - bytable.deserialize(buffer.subarray(offset)); - return bytable; - } + protected deserializeFields(buffer: Buffer) { + this.buffer = new DataView(Uint8Array.from(buffer).buffer); + } - override serialize() { - if (!this.buffer || this.buffer.byteLength === 0) { - throw new Error('Cannot serialize empty buffer'); - } - return Buffer.from(this.buffer.buffer); - } + override deserialize(buffer: Buffer) { + validateBuffer(buffer, 'deserialize'); + return this.deserializeFields(buffer); + } - override deserialize(buffer: Buffer) { - if (!buffer || buffer.length === 0) { - throw new Error('Cannot deserialize empty buffer'); - } - this.buffer = new DataView(Uint8Array.from(buffer).buffer); - } + protected serializeFields(): Buffer { + return Buffer.from(this.buffer.buffer); + } - get json() { - return { - name: this.name_, - serializeSize: this.serializeSize, - }; - } + override serialize(): Buffer { + validateBuffer(this.buffer, 'serialize'); + return this.serializeFields(); + } - get serializeSize() { - return this.buffer.byteLength; - } + get json() { + return { + name: this.name_, + serializeSize: this.serializeSize, + }; + } - setName(name: string) { - this.name_ = name; - } + override get serializeSize(): number { + return this.buffer.byteLength; + } - get name() { - return this.name_; - } + setName(name: string) { + this.name_ = name; + } - get value() { - return this.value_; - } + get name() { + return this.name_; + } + get value() { + return this.value_; + } + setValue(value: string | number | Buffer) { + this.validateValue(value); + this.value_ = value; + } - setValue(value: string | number | Buffer) { - this.validateValue(value); - this.value_ = value; - } + override toString() { + return `BytableBase { name: ${this.name_}, value: ${this.value_} }`; + } } +/** + * Validates that the provided buffer is defined and non-empty. + * + * @param buf - The buffer to validate. + * @param direction - A string describing the operation being performed (e.g., "serialize" or "deserialize"). + * + * @throws {Error} If {@link buf} is undefined or has zero byte length. + */ +export function validateBuffer( + buf: DataView | ArrayBufferLike, + direction: string, +) { + if (typeof buf === 'undefined') { + throw new Error(`Cannot ${direction} undefined buffer`); + } + + if (buf.byteLength === 0) { + throw new Error(`Cannot ${direction} empty buffer`); + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableBase.test.ts b/libs/@rustymotors/binary/src/lib/BytableBase.test.ts new file mode 100644 index 000000000..0b060b37f --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableBase.test.ts @@ -0,0 +1,154 @@ +import { describe, it, expect } from 'vitest'; +import { BytableBase } from './BytableBase'; + +class TestBytable extends BytableBase { + override toString() { + return 'TestBytable'; + } + + override deserialize(buffer: Buffer) { + this.buffer = new DataView( + buffer.buffer, + buffer.byteOffset, + buffer.byteLength, + ); + } + + override serialize() { + return Buffer.from(this.buffer.buffer); + } + + override get serializeSize() { + return this.buffer.byteLength; + } +} + +describe('BytableBase', () => { + describe('validateValue', () => { + it('should throw an error for NaN values', () => { + const instance = new TestBytable(); + expect(() => instance['validateValue'](NaN)).toThrow( + 'Cannot set NaN value', + ); + }); + + it('should throw an error for empty buffers', () => { + const instance = new TestBytable(); + expect(() => instance['validateValue'](Buffer.alloc(0))).toThrow( + 'Cannot set empty buffer', + ); + }); + + it('should not throw for valid values', () => { + const instance = new TestBytable(); + expect(() => instance['validateValue'](42)).not.toThrow(); + expect(() => instance['validateValue']('valid')).not.toThrow(); + expect(() => + instance['validateValue'](Buffer.from('valid')), + ).not.toThrow(); + }); + }); + + describe('getByteLength', () => { + it('should return correct byte length for buffers', () => { + const instance = new TestBytable(); + const buffer = Buffer.from('test'); + expect(instance['getByteLength'](buffer)).toBe(buffer.byteLength); + }); + + it('should return correct byte length for strings', () => { + const instance = new TestBytable(); + expect(instance['getByteLength']('test')).toBe( + Buffer.from('test').byteLength, + ); + }); + + it('should return correct byte length for numbers', () => { + const instance = new TestBytable(); + expect(instance['getByteLength'](123)).toBe( + Buffer.from('123').byteLength, + ); + }); + }); + + describe('validateString', () => { + it('should throw an error for empty strings', () => { + const instance = new TestBytable(); + expect(() => instance['validateString']('')).toThrow( + 'Cannot set empty string', + ); + }); + + it('should not throw for non-empty strings', () => { + const instance = new TestBytable(); + expect(() => instance['validateString']('valid')).not.toThrow(); + }); + }); + + describe('toBuffer', () => { + it('should return the same buffer if input is a buffer', () => { + const instance = new TestBytable(); + const buffer = Buffer.from('test'); + expect(instance['toBuffer'](buffer)).toBe(buffer); + }); + + it('should convert strings to buffers', () => { + const instance = new TestBytable(); + const str = 'test'; + expect(instance['toBuffer'](str)).toEqual(Buffer.from(str)); + }); + + it('should convert numbers to buffers', () => { + const instance = new TestBytable(); + const num = 123; + expect(instance['toBuffer'](num)).toEqual(Buffer.from('123')); + }); + }); + + describe('align8', () => { + it('should align values to the next multiple of 8', () => { + const instance = new TestBytable(); + expect(instance['align8'](5)).toBe(8); + expect(instance['align8'](8)).toBe(8); + expect(instance['align8'](10)).toBe(16); + }); + }); + + describe('getUint16', () => { + it('should read a 16-bit unsigned integer from the buffer', () => { + const instance = new TestBytable(); + const buffer = Buffer.alloc(2); + buffer.writeUInt16BE(42, 0); + instance.deserialize(buffer); + expect(instance['getUint16'](0, false)).toBe(42); + }); + }); + + describe('getUint32', () => { + it('should read a 32-bit unsigned integer from the buffer', () => { + const instance = new TestBytable(); + const buffer = Buffer.alloc(4); + buffer.writeUInt32BE(42, 0); + instance.deserialize(buffer); + expect(instance['getUint32'](0, false)).toBe(42); + }); + }); + + describe('abstract methods', () => { + it('should throw errors for unimplemented methods', () => { + const instance = new BytableBase(); + expect(() => instance.toString()).toThrow( + 'Method should be implemented by subclass', + ); + expect(() => instance.deserialize(Buffer.alloc(0))).toThrow( + 'Method should be implemented by subclass', + ); + expect(() => instance.serialize()).toThrow( + 'Method should be implemented by subclass', + ); + expect(() => instance.serializeSize).toThrow( + 'Method should be implemented by subclass', + ); + }); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableBase.ts b/libs/@rustymotors/binary/src/lib/BytableBase.ts index d63223212..a899c529e 100644 --- a/libs/@rustymotors/binary/src/lib/BytableBase.ts +++ b/libs/@rustymotors/binary/src/lib/BytableBase.ts @@ -1,71 +1,148 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +/** + * The `BytableBase` class serves as a base class for handling binary data operations. + * It provides utility methods for working with buffers, validating values, and + * converting data to and from binary formats. Subclasses are expected to implement + * specific serialization and deserialization logic. + * + * @remarks + * This class is designed to be extended and should not be used directly. + * Subclasses must override the `toString`, `deserialize`, `serialize`, and + * `serializeSize` methods to provide specific functionality. + * + * @example + * ```typescript + * class MyBytable extends BytableBase { + * toString() { + * // Implement string representation logic + * } + * deserialize(buffer: Buffer) { + * // Implement deserialization logic + * } + * serialize() { + * // Implement serialization logic + * } + * get serializeSize() { + * // Return the size of the serialized data + * } + * } + * ``` + */ export class BytableBase { - protected buffer: DataView = new DataView(new ArrayBuffer(0)); + protected buffer: DataView = new DataView(new ArrayBuffer(1)); - getUint16( - this: BytableBase, - offset: number, - littleEndian: boolean = false, - ) { - return this.buffer.getUint16(offset, littleEndian); - } + protected getUint16( + this: BytableBase, + offset: number, + littleEndian = false, + ) { + return this.buffer.getUint16(offset, littleEndian); + } - getUint32(this: BytableBase, offset: number, littleEndian: boolean = false) { - return this.buffer.getUint32(offset, littleEndian); - } + protected getUint32( + this: BytableBase, + offset: number, + littleEndian = false, + ) { + return this.buffer.getUint32(offset, littleEndian); + } - toString(this: BytableBase) { - return this.buffer.toString(); - } + toString(this: BytableBase) { + throw new Error('Method should be implemented by subclass'); + } - deserialize(this: BytableBase, buffer: Buffer) { - if (buffer.length === 0) { - throw new Error("Cannot deserialize empty buffer"); - } - this.buffer = new DataView(Uint8Array.from(buffer).buffer); - } + deserialize(this: BytableBase, _buffer: Buffer) { + throw new Error('Method should be implemented by subclass'); + } - serialize(this: BytableBase) { - return Buffer.from(this.buffer.buffer); - } + serialize(this: BytableBase) { + throw new Error('Method should be implemented by subclass'); + } - /** - * Validate the value of the container. - * @param value - The value to validate. - * @returns void - * @throws Error if the value is NaN or an empty buffer - */ - protected validateValue(value: string | number | Buffer) { - if (typeof value === "number" && Number.isNaN(value)) { - throw new Error("Cannot set NaN value"); - } - if (value instanceof Buffer && value.length === 0) { - throw new Error("Cannot set empty buffer"); - } - } + /** + * Validate the value of the container. + * @param value - The value to validate. + * @returns void + * @throws Error if the value is NaN or an empty buffer + */ + protected validateValue(value: string | number | Buffer) { + if (typeof value === 'number' && Number.isNaN(value)) { + throw new Error('Cannot set NaN value'); + } + if (value instanceof Buffer && value.length === 0) { + throw new Error('Cannot set empty buffer'); + } + } - /** - * Get the byte length of the value. - * @param value - The value to get the byte length of. - * @returns The byte length of the value. - */ - protected getByteLength(value: string | number | Buffer) { - if (value instanceof Buffer) { - return value.byteLength; - } - // Convert strings and numbers to Buffer to get correct byte length - return Buffer.from(String(value)).byteLength; - } + /** + * Get the byte length of the value. + * @param value - The value to get the byte length of. + * @returns The byte length of the value. + */ + protected getByteLength(value: string | number | Buffer) { + if (value instanceof Buffer) { + return value.byteLength; + } + // Convert strings and numbers to Buffer to get correct byte length + return Buffer.from(String(value)).byteLength; + } - protected validateString(value: string) { - if (value.length === 0) { - throw new Error("Cannot set empty string"); - } - } + protected validateString(value: string) { + if (value.length === 0) { + throw new Error('Cannot set empty string'); + } + } - protected toBuffer(value: string | number | Buffer) { - if (value instanceof Buffer) { - return value; - } - return Buffer.from(String(value)); + protected toBuffer(value: string | number | Buffer) { + if (value instanceof Buffer) { + return value; } - } + return Buffer.from(String(value)); + } + + protected align8(value: number) { + return Math.ceil(value / 8) * 8; + } + + get serializeSize(): number { + throw new Error('Method should be implemented by subclass'); + } +} + +/** + * Converts a string, number, or Buffer to a Buffer. + * + * If the input is a string, returns a Buffer containing the string's bytes. If the input is a number, returns a 4-byte Buffer containing the number as a 32-bit unsigned integer in little-endian format. If the input is already a Buffer, returns it unchanged. + * + * @param value - The value to convert. + * @returns A Buffer representation of the input value. + */ +export function coerceValueToBuffer( + value: string | number | Buffer, +) { + let coercedValue: Buffer; + if (typeof value === 'string') { + coercedValue = Buffer.from(value); + } else if (typeof value === 'number') { + coercedValue = Buffer.alloc(4); + coercedValue.writeUInt32LE(value, 0); + } else { + coercedValue = value; + } + return coercedValue; +} diff --git a/libs/@rustymotors/binary/src/lib/BytableBuffer.ts b/libs/@rustymotors/binary/src/lib/BytableBuffer.ts new file mode 100644 index 000000000..acba822d0 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableBuffer.ts @@ -0,0 +1,66 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableObject } from './types.js'; + +export class BytableBuffer implements BytableObject { + protected name_ = ''; + protected value_: Buffer = Buffer.alloc(0); + + deserialize(buffer: Buffer) { + this.value_ = buffer; + } + + get serializeSize() { + return this.value_.length; + } + + serialize() { + return this.value_; + } + + get json() { + return { + name: this.name_, + serializeSize: this.serializeSize, + value: this.value_.toString('hex'), + }; + } + + setName(name: string) { + this.name_ = name; + } + + get name() { + return this.name_; + } + + get value() { + return this.value_; + } + + setValue(value: Buffer) { + this.value_ = value; + } + + getUint16(offset: number, _littleEndian: boolean): number { + return this.value_.readUInt16BE(offset); + } + + getUint32(offset: number, _littleEndian: boolean): number { + return this.value_.readUInt32BE(offset); + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableByte.test.ts b/libs/@rustymotors/binary/src/lib/BytableByte.test.ts new file mode 100644 index 000000000..673a32fa4 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableByte.test.ts @@ -0,0 +1,90 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { describe, it, expect } from 'vitest'; +import { BytableByte } from './BytableByte.js'; + +describe('BytableByte', () => { + it('should serialize correctly', () => { + const bytable = new BytableByte(); + bytable.setValue(255); + const buffer = bytable.serialize(); + expect(buffer).toBeInstanceOf(Buffer); + expect(buffer.length).toBe(1); + expect(buffer.readUInt8(0)).toBe(255); + }); + + it('should deserialize correctly', () => { + const bytable = new BytableByte(); + const buffer = Buffer.from([128]); + bytable.deserialize(buffer); + expect(bytable.value).toBe(128); + }); + + it('should throw an error when deserializing a buffer with insufficient length', () => { + const bytable = new BytableByte(); + const buffer = Buffer.alloc(0); + expect(() => bytable.deserialize(buffer)).toThrow( + 'Cannot deserialize buffer with insufficient length', + ); + }); + + it('should return correct serializeSize', () => { + const bytable = new BytableByte(); + expect(bytable.serializeSize).toBe(1); + }); + + it('should return correct JSON representation', () => { + const bytable = new BytableByte(); + bytable.setValue(42); + bytable.setName('TestByte'); + expect(bytable.json).toEqual({ + name: 'TestByte', + serializeSize: 1, + value: 42, + }); + }); + + it('should convert to string correctly', () => { + const bytable = new BytableByte(); + bytable.setValue(42); + bytable.setName('TestByte'); + expect(bytable.toString()).toBe( + JSON.stringify({ + name: 'TestByte', + serializeSize: 1, + value: 42, + }), + ); + }); + + it('should set and get name correctly', () => { + const bytable = new BytableByte(); + bytable.setName('MyByte'); + expect(bytable.name).toBe('MyByte'); + }); + + it('should set and get value correctly', () => { + const bytable = new BytableByte(); + bytable.setValue(100); + expect(bytable.value).toBe(100); + }); + + it('should throw an error when setting an invalid value type', () => { + const bytable = new BytableByte(); + expect(() => bytable.setValue('invalid')).toThrow('Invalid value type'); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableByte.ts b/libs/@rustymotors/binary/src/lib/BytableByte.ts new file mode 100644 index 000000000..89f9a04d2 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableByte.ts @@ -0,0 +1,75 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableBase } from './BytableBase.js'; +import { BytableObject } from './types.js'; + +export class BytableByte extends BytableBase implements BytableObject { + protected value_ = 0; + protected name_ = ''; + + override deserialize(buffer: Buffer) { + if (buffer.length < 1) { + throw new Error( + 'Cannot deserialize buffer with insufficient length', + ); + } + this.value_ = buffer.readUInt8(0); + return this; + } + + override get serializeSize() { + return 1; + } + + override serialize() { + const buffer = Buffer.alloc(1); + buffer.writeUInt8(this.value_, 0); + return buffer; + } + + get json() { + return { + name: this.name_, + serializeSize: this.serializeSize, + value: this.value_, + }; + } + + setName(name: string) { + this.name_ = name; + } + + override toString() { + return JSON.stringify(this.json); + } + + get name() { + return this.name_; + } + + get value() { + return this.value_; + } + + setValue(value: string | number | Buffer) { + if (typeof value === 'number') { + this.value_ = value; + } else { + throw new Error('Invalid value type'); + } + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableCString.test.ts b/libs/@rustymotors/binary/src/lib/BytableCString.test.ts new file mode 100644 index 000000000..e89ca1ec9 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableCString.test.ts @@ -0,0 +1,117 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { describe, beforeEach, it, expect } from 'vitest'; +import { BytableCString } from './BytableCString.js'; + +describe('BytableCString', () => { + let cstring: BytableCString; + + beforeEach(() => { + cstring = new BytableCString(); + }); + + it('should set and get value correctly', () => { + const value = 'test'; + cstring.setValue(value); + expect(cstring.getValue()).toBe(value); + }); + + it('should throw error when setting null termination', () => { + expect(() => cstring.setNullTerminated(false)).toThrowError( + 'Cannot set null termination for CString', + ); + }); + + it('should return true for null termination by default', () => { + expect(cstring.getNullTerminated()).toBe(true); + }); + + it('should throw error when setting length for null-terminated container', () => { + expect(() => cstring.setLength(10)).toThrowError( + 'Cannot set length for null terminated container', + ); + }); + + it('should calculate serialize size correctly', () => { + cstring.setValue('test'); + expect(cstring.serializeSize).toBe(5); // 4 characters + 1 null terminator + }); + + it('should serialize correctly', () => { + cstring.setValue('test'); + const serialized = cstring.serialize(); + expect(serialized).toEqual(Buffer.from('test\0')); + }); + + it('should serialize empty value correctly', () => { + cstring.setValue(''); + const serialized = cstring.serialize(); + expect(serialized).toEqual(Buffer.from('\0')); + }); + + it('should throw error when serializing non-null-terminated container', () => { + cstring.setValue('test'); + (cstring as any).nullTerminated = false; // Force nullTerminated to false for testing + expect(() => cstring.serialize()).toThrowError( + 'Cannot serialize CString', + ); + }); + + it('should deserialize correctly', () => { + const buffer = Buffer.from('test\0'); + cstring.deserialize(buffer); + expect(cstring.getValue()).toBe('test'); + expect(cstring.getLength()).toBe(4); // Corrected to 4 characters (excluding null terminator) + }); + + it('should throw error when deserializing non-null-terminated container', () => { + const buffer = Buffer.from('test'); + (cstring as any).nullTerminated = false; // Force nullTerminated to false for testing + expect(() => cstring.deserialize(buffer)).toThrowError( + 'Cannot deserialize CString', + ); + }); + + it('should return correct JSON representation', () => { + cstring.setValue('test'); + const json = cstring.json; + expect(json).toEqual({ + value: 'test', + length: 4, + nullTerminated: true, + serializeSize: 5, + }); + }); + + it('should set and get name correctly', () => { + const name = 'testName'; + cstring.setName(name); + expect(cstring.name).toBe(name); + }); + + it('should return value when accessing "value" getter', () => { + const value = 'test'; + cstring.setValue(value); + expect(cstring.value).toBe(value); + }); + + it('should return string representation correctly', () => { + const value = 'test'; + cstring.setValue(value); + expect(cstring.toString()).toBe(value); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableCString.ts b/libs/@rustymotors/binary/src/lib/BytableCString.ts new file mode 100644 index 000000000..0f606f42f --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableCString.ts @@ -0,0 +1,131 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableBase } from './BytableBase.js'; +import { BytableObject } from './types.js'; + +export class BytableCString extends BytableBase implements BytableObject { + private value_: string | number | Buffer = ''; + private nullTerminated = true; + private length = 0; + private name_ = ''; + + /** + * Set the value of the container. + * @param value - The value to set. + * @returns void + */ + setValue(value: string | number | Buffer) { + this.value_ = value; + this.length = this.getByteLength(value); + } + + getValue() { + return this.value_; + } + + setNullTerminated(_nullTerminated: boolean) { + throw new Error('Cannot set null termination for CString'); + } + + getNullTerminated() { + return this.nullTerminated; + } + + /** + * Set the length of the container. + * @param length - The length of the container. + * @returns void + * @throws Error if the container is set to null terminated + */ + setLength(length: number) { + if (this.nullTerminated) { + throw new Error('Cannot set length for null terminated container'); + } else { + this.length = length; + } + } + + getLength() { + return this.length; + } + + override get serializeSize() { + if (this.nullTerminated) { + return this.length + 1; + } else { + throw new Error('Cannot get serialize size for CString'); + } + } + + /** + * Serialize the container. + * @returns Buffer - The serialized container. + */ + override serialize() { + const value = this.toBuffer(this.value_); + if (this.nullTerminated) { + return value.length === 0 + ? Buffer.from('\0') + : Buffer.concat([value, Buffer.from('\0')]); + } else { + throw new Error('Cannot serialize CString'); + } + } + + /** + * Deserialize the container. + * @param buffer - The buffer to deserialize. + * @returns void + */ + override deserialize(buffer: Buffer) { + if (this.nullTerminated) { + let length = 0; + while (buffer[length] !== 0) { + length++; + } + this.setValue(buffer.subarray(0, length).toString('utf-8')); + this.length = length; // Exclude the null terminator from the length + } else { + throw new Error('Cannot deserialize CString'); + } + } + + get json() { + return { + value: this.value_, + length: this.length, + nullTerminated: this.nullTerminated, + serializeSize: this.serializeSize, + }; + } + + setName(name: string) { + this.name_ = name; + } + + get name() { + return this.name_; + } + + get value() { + return this.value_; + } + + override toString(): string { + return this.value_.toString(); + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableContainer.test.ts b/libs/@rustymotors/binary/src/lib/BytableContainer.test.ts new file mode 100644 index 000000000..24e4a3d16 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableContainer.test.ts @@ -0,0 +1,92 @@ +import { describe, it, expect } from 'vitest'; +import { BytableContainer, BytableShortContainer } from './BytableContainer.js'; + +const str = 'hello'; +const buf = Buffer.from(str); + +describe('BytableShortContainer', () => { + it('sets and gets value (string, number, Buffer)', () => { + const c = new BytableShortContainer(); + c.setValue(str); + expect(c.getValue()).toBe(str); + c.setValue(42); + expect(c.getValue()).toBe(42); + c.setValue(buf); + expect(c.getValue()).toEqual(buf); + }); + + it('throws if setLength on nullTerminated', () => { + const c = new BytableShortContainer(); + expect(() => c.setLength(5)).not.toThrow(); + // setNullTerminated is not implemented, so skip that path + }); + + it('serialize/deserialize roundtrip (non-nullTerminated)', () => { + const c = new BytableShortContainer(); + c.setValue(str); + const ser = c.serialize(); + const c2 = new BytableShortContainer(); + c2.deserialize(ser); + expect(c2.getValue()).toBe(str); + }); +}); + +describe('BytableContainer', () => { + it('sets and gets value (string, number, Buffer)', () => { + const c = new BytableContainer(); + c.setValue(str); + expect(c.getValue()).toBe(str); + c.setValue(42); + expect(c.getValue()).toBe(42); + c.setValue(buf); + expect(c.getValue()).toEqual(buf); + }); + + it('setNullTerminated and getNullTerminated', () => { + const c = new BytableContainer(); + c.setNullTerminated(true); + expect(c.getNullTerminated()).toBe(true); + c.setNullTerminated(false); + expect(c.getNullTerminated()).toBe(false); + }); + + it('throws if setLength on nullTerminated', () => { + const c = new BytableContainer(); + c.setNullTerminated(true); + expect(() => c.setLength(5)).toThrow('Cannot set length for null terminated container'); + }); + + it('serialize/deserialize roundtrip (non-nullTerminated)', () => { + const c = new BytableContainer(); + c.setValue(str); + const ser = c.serialize(); + const c2 = new BytableContainer(); + c2.deserialize(ser); + expect(c2.getValue()).toBe(str); + }); + + it('serialize/deserialize roundtrip (nullTerminated)', () => { + const c = new BytableContainer(); + c.setNullTerminated(true); + c.setValue(str); + const ser = c.serialize(); + const c2 = new BytableContainer(); + c2.setNullTerminated(true); + c2.deserialize(ser); + expect(c2.getValue()).toBe(str); + }); + + it('json property returns expected structure', () => { + const c = new BytableContainer(); + c.setValue(str); + c.setNullTerminated(false); + c.setName('foo'); + const json = c.json; + expect(json).toMatchObject({ + value: str, + length: str.length, + nullTerminated: false, + serializeSize: str.length + 4, + }); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableContainer.ts b/libs/@rustymotors/binary/src/lib/BytableContainer.ts index 1e0484882..7b6b33aef 100644 --- a/libs/@rustymotors/binary/src/lib/BytableContainer.ts +++ b/libs/@rustymotors/binary/src/lib/BytableContainer.ts @@ -1,139 +1,204 @@ -import { BytableBase } from "./BytableBase"; -import { BytableObject } from "./types"; - -export class BytableContainer extends BytableBase implements BytableObject { - private value_: string | number | Buffer = ""; - private nullTerminated: boolean = false; - private length: number = 0; - private name_: string = ""; - static fromBuffer( - buffer: Buffer, - offset: number, - nullTerminated: boolean = false, - length: number = 0, - ) { - const container = new this(); - container.setNullTerminated(nullTerminated); - container.setLength(length); - container.deserialize(buffer.subarray(offset)); - return container; - } - - /** - * Set the value of the container. - * @param value - The value to set. - * @returns void - * @throws Error if the container is null terminated and the value is an empty string - */ - setValue(value: string | number | Buffer) { - this.validateValue(value); - if (this.nullTerminated && typeof value === "string") { - this.validateString(value); - } - this.value_ = value; - this.length = this.getByteLength(value); - } - - getValue() { - return this.value_; - } - - setNullTerminated(nullTerminated: boolean) { - this.nullTerminated = nullTerminated; - } - - getNullTerminated() { - return this.nullTerminated; - } - - /** - * Set the length of the container. - * @param length - The length of the container. - * @returns void - * @throws Error if the container is set to null terminated - */ - setLength(length: number) { - if (this.nullTerminated) { - throw new Error("Cannot set length for null terminated container"); - } else { - this.length = length; - } - } - - getLength() { - return this.length; - } - - get serializeSize() { - if (this.nullTerminated) { - return this.length + 1; - } else { - return this.length + 2; - } - } - - - - /** - * Serialize the container. - * @returns Buffer - The serialized container. - */ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableBase } from './BytableBase.js'; +import { BytableObject } from './types.js'; + +abstract class BytableContainerBase extends BytableBase implements BytableObject { + protected value_: string | number | Buffer = ''; + protected nullTerminated = false; + protected length = 0; + protected name_ = ''; + + abstract get json(): Record; + abstract override get serializeSize(): number; + abstract override serialize(): Buffer; + abstract override deserialize(buffer: Buffer): void; + + setValue(value: string | number | Buffer): void { + this.validateValue(value); + if (this.nullTerminated && typeof value === 'string') { + this.validateString(value); + } + this.value_ = value; + this.length = this.getByteLength(value); + } + + getValue(): string | number | Buffer { + return this.value_; + } + + setNullTerminated(nullTerminated: boolean): void { + this.nullTerminated = nullTerminated; + } + + getNullTerminated(): boolean { + return this.nullTerminated; + } + + /** + * Set the length of the container. + * @param length - The length of the container. + * @returns void + * @throws Error if the container is set to null terminated + */ + setLength(length: number): void { + if (this.nullTerminated) { + throw new Error('Cannot set length for null terminated container'); + } else { + this.length = length; + } + } + + getLength(): number { + return this.length; + } + + setName(name: string): void { + this.name_ = name; + } + + get name(): string { + return this.name_; + } + + get value(): string | number | Buffer { + return this.value_; + } + + override toString(): string { + throw new Error('Method not implemented.'); + } +} + +export class BytableShortContainer extends BytableContainerBase { + override get serializeSize() { + return this.nullTerminated ? this.length + 1 : this.length + 2; + } + + /** + * Serialize the container. + * @returns Buffer - The serialized container. + */ + override serialize() { + const value = this.toBuffer(this.value_); + if (this.nullTerminated) { + return value.length === 0 + ? Buffer.from('\0') + : Buffer.concat([value, Buffer.from('\0')]); + } else { + const lengthPrefix = Buffer.alloc(2); + lengthPrefix.writeUInt16BE(this.length, 0); + return Buffer.concat([lengthPrefix, value]); + } + } + + /** + * Deserialize the container. + * @param buffer - The buffer to deserialize. + * @returns void + */ + override deserialize(buffer: Buffer) { + const offset = 0; + if (this.nullTerminated) { + // Find the first null byte (0x00) + const nullIdx = buffer.indexOf(0, offset); + if (nullIdx === -1) { + throw new Error('Null terminator not found in buffer'); + } + const str = buffer.subarray(offset, nullIdx).toString('utf-8'); + this.setValue(str); + this.length = Buffer.from(str).length; + } else { + const length = buffer.readUInt16BE(offset); + this.setValue( + buffer + .subarray(offset + 2, offset + length + 2) + .toString('utf-8'), + ); + this.length = length; + } + } + + get json() { + return { + name: this.name_, + value: this.value_, + length: this.length, + nullTerminated: this.nullTerminated, + serializeSize: this.serializeSize, + }; + } +} + +export class BytableContainer extends BytableContainerBase { + override get serializeSize() { + return this.nullTerminated ? this.length + 1 : this.length + 4; + } + + /** + * Serialize the container. + * @returns Buffer - The serialized container. + */ override serialize() { const value = this.toBuffer(this.value_); - if (this.nullTerminated) { - return value.length === 0 ? Buffer.from("\0") : Buffer.concat([value, Buffer.from("\0")]); - } else { - const lengthPrefix = Buffer.alloc(2); - lengthPrefix.writeUInt16BE(this.length, 0); - return Buffer.concat([lengthPrefix, value]); - } - } - - /** - * Deserialize the container. - * @param buffer - The buffer to deserialize. - * @returns void - */ - override deserialize(buffer: Buffer) { - const offset = 0; - if (this.nullTerminated) { - let length = 0; - let cursor = 0; - do { - this.setValue( - buffer.subarray(offset, offset + length).toString("utf-8"), - ); - cursor++; - } while (buffer[offset + cursor] !== 0); - this.setValue(buffer.subarray(offset, offset + length).toString("utf-8")); - this.length = length + 1; - } else { - const length = buffer.readUInt16BE(offset); - this.setValue( - buffer.subarray(offset + 2, offset + length + 2).toString("utf-8"), - ); - this.length = length; - } - } - - get json() { - return { - value: this.value_, - length: this.length, - nullTerminated: this.nullTerminated, - serializeSize: this.serializeSize, - }; - } - - setName(name: string) { - this.name_ = name; - } - - get name() { - return this.name_; - } - - get value() { - return this.value_; - } + if (this.nullTerminated) { + return value.length === 0 + ? Buffer.from('\0') + : Buffer.concat([value, Buffer.from('\0')]); + } else { + const lengthPrefix = Buffer.alloc(4); + lengthPrefix.writeUInt32BE(this.length, 0); + return Buffer.concat([lengthPrefix, value]); + } + } + + /** + * Deserialize the container. + * @param buffer - The buffer to deserialize. + * @returns void + */ + override deserialize(buffer: Buffer) { + const offset = 0; + if (this.nullTerminated) { + // Find the first null byte (0x00) + const nullIdx = buffer.indexOf(0, offset); + if (nullIdx === -1) { + throw new Error('Null terminator not found in buffer'); + } + const str = buffer.subarray(offset, nullIdx).toString('utf-8'); + this.setValue(str); + this.length = Buffer.from(str).length; + } else { + const length = buffer.readUInt32BE(offset); + this.setValue( + buffer + .subarray(offset + 4, offset + length + 4) + .toString('utf-8'), + ); + this.length = length; + } + } + + get json() { + return { + value: this.value_, + length: this.length, + nullTerminated: this.nullTerminated, + serializeSize: this.serializeSize, + }; + } } diff --git a/libs/@rustymotors/binary/src/lib/BytableData.test.ts b/libs/@rustymotors/binary/src/lib/BytableData.test.ts new file mode 100644 index 000000000..9291583cb --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableData.test.ts @@ -0,0 +1,208 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { describe, it, expect, beforeEach } from 'vitest'; +import { BytableData } from './BytableData.js'; +import { BytableFieldTypes } from './BytableMessage.js'; +import { BytableObject } from './types.js'; + +// Extend BytableFieldTypes to include MockField +interface ExtendedBytableFieldTypes { + MockField: typeof MockField; + MockCStringField: typeof MockCStringField; +} + +declare module './BytableMessage.js' { + interface BytableFieldTypes extends ExtendedBytableFieldTypes {} +} + +class MockField implements BytableObject { + name = ''; + value: string | number | Buffer = ''; + serializeSize = 4; + + setName(name: string) { + this.name = name; + } + + deserialize(buffer: Buffer) { + this.value = buffer.subarray(0, 4).toString(); + } + + serialize() { + let str = String(this.value); + if (str.length > 4) str = str.slice(0, 4); + while (str.length < 4) str += '\0'; + return Buffer.from(str); + } + + setValue(value: string | number | Buffer) { + this.value = value; + } + + get json() { + return { name: this.name, value: this.value }; + } +} + +class MockCStringField implements BytableObject { + name = ''; + value: string | number | Buffer = ''; + + setName(name: string) { + this.name = name; + } + + deserialize(buffer: Buffer) { + const nullIdx = buffer.indexOf(0); + if (nullIdx === -1) { + this.value = buffer.toString(); + } else { + this.value = buffer.subarray(0, nullIdx).toString(); + } + } + + serialize() { + let str = String(this.value); + const buf = Buffer.alloc(str.length + 1); + buf.write(str, 0, 'utf8'); + buf[str.length] = 0; + return buf; + } + + setValue(value: string | number | Buffer) { + this.value = value; + } + + get json() { + return { name: this.name, value: this.value }; + } + + get serializeSize() { + return String(this.value).length + 1; + } +} + +// Explicitly cast MockField to bypass type-checking errors +(BytableFieldTypes as any)['MockField'] = MockField; + +// Register MockCStringField for tests +(BytableFieldTypes as any)['MockCStringField'] = MockCStringField; + +const setupBytableData = ( + fields: { name: string; field: any }[], + values: Record, +) => { + const bytableData = new BytableData(); + bytableData.setSerializeOrder(fields); + for (const [name, value] of Object.entries(values)) { + bytableData.setFieldValueByName(name, value); + } + return bytableData; +}; + +describe('BytableData', () => { + let bytableData: BytableData; + beforeEach(() => { + bytableData = new BytableData(); + }); + + it('should serialize and deserialize correctly (MockField)', () => { + const fields = [ + { name: 'field1', field: 'MockField' as any }, + { name: 'field2', field: 'MockField' as any }, + ]; + const values = { field1: 'test1', field2: 'test2' }; + const data = setupBytableData(fields, values); + const serialized = data.serialize(); + const deserializedData = new BytableData(); + deserializedData.setSerializeOrder(fields); + deserializedData.deserialize(serialized); + expect(deserializedData.getFieldValueByName('field1')).toBe('test'); + expect(deserializedData.getFieldValueByName('field2')).toBe('test'); + }); + + it('should serialize and deserialize correctly (MockCStringField)', () => { + const fields = [ + { name: 'cstring1', field: 'MockCStringField' as any }, + { name: 'cstring2', field: 'MockCStringField' as any }, + ]; + const values = { cstring1: 'hello', cstring2: 'world' }; + const data = setupBytableData(fields, values); + const serialized = data.serialize(); + const deserializedData = new BytableData(); + deserializedData.setSerializeOrder(fields); + deserializedData.deserialize(serialized); + expect(deserializedData.getFieldValueByName('cstring1')).toBe('hello'); + expect(deserializedData.getFieldValueByName('cstring2')).toBe('world'); + }); + + it('should calculate serializeSize correctly', () => { + const fields = [ + { name: 'field1', field: 'MockField' as any }, + { name: 'field2', field: 'MockField' as any }, + ]; + const values = { field1: 'test1', field2: 'test2' }; + const data = setupBytableData(fields, values); + expect(data.serializeSize).toBe(8); // 4 bytes per field + }); + + it('should throw an error for unknown field types during deserialization', () => { + expect(() => + bytableData.setSerializeOrder([ + { + name: 'field1', + field: 'UnknownField' as keyof typeof BytableFieldTypes, + }, + ]), + ).toThrowError('Unknown field type: UnknownField'); + }); + + it('should return the correct JSON representation', () => { + const fields = [{ name: 'field1', field: 'MockField' as any }]; + const values = { field1: 'test1' }; + const data = setupBytableData(fields, values); + expect(data.json).toEqual({ + name: 'BytableData', + serializeSize: 4, + fields: [{ name: 'field1', value: 'test1' }], + }); + }); + + it('should throw an error when accessing a non-existent field by name', () => { + expect(() => + bytableData.getFieldValueByName('nonExistent'), + ).toThrowError('Field nonExistent not found'); + }); + + it('should throw an error when setting a value for a non-existent field', () => { + expect(() => + bytableData.setFieldValueByName('nonExistent', 'value'), + ).toThrowError('Field nonExistent not found'); + }); + + it('should allow setting and getting the name', () => { + bytableData.setName('TestName'); + expect(bytableData.name).toBe('TestName'); + }); + + it('should throw an error for unimplemented value getter and setter', () => { + expect(() => bytableData.value).toThrowError('Not implemented'); + expect(() => bytableData.setValue('value')).toThrowError( + 'Not implemented', + ); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableData.ts b/libs/@rustymotors/binary/src/lib/BytableData.ts new file mode 100644 index 000000000..d7fe670b0 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableData.ts @@ -0,0 +1,142 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableBase } from './BytableBase.js'; +import { BytableFieldTypes } from './BytableMessage.js'; +import { BytableObject } from './types.js'; + +export class BytableData extends BytableBase implements BytableObject { + protected name_ = 'BytableData'; + protected fields_: Array = []; + protected serializeOrder_: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }> = []; + + constructor() { + super(); + } + + override deserialize(buffer: Buffer) { + try { + this.fields_ = []; // Clear fields before deserialization + let offset = 0; + for (const field of this.serializeOrder_) { + if (!(field.field in BytableFieldTypes)) { + throw new Error(`Unknown field type: ${field.field}`); + } + + const fieldType = BytableFieldTypes[field.field]; + const fieldInstance = new fieldType(); + fieldInstance.setName(field.name); + fieldInstance.deserialize(buffer.subarray(offset)); + this.fields_.push(fieldInstance); + offset += fieldInstance.serializeSize; + } + } catch (error) { + const err = new Error( + `Error deserializing message: ${(error as Error).message}`, + { + cause: error, + }, + ); + throw err; + } + } + + override get serializeSize() { + const fieldSizes = this.fields_.map((field) => field.serializeSize); + return fieldSizes.reduce((a, b) => a + b, 0); + } + + override serialize() { + const buffer = Buffer.alloc(this.serializeSize); + let offset = 0; + + for (const field of this.fields_) { + buffer.set(field.serialize(), offset); + offset += field.serializeSize; + } + return buffer; + } + + get json() { + return { + name: this.name, + serializeSize: this.serializeSize, + fields: this.fields_.map((field) => field.json), + }; + } + + setName(name: string) { + this.name_ = name; + } + + override toString() { + return JSON.stringify(this.json); + } + + setSerializeOrder( + serializeOrder: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }>, + ) { + this.serializeOrder_ = serializeOrder; + this.fields_ = serializeOrder.map(({ name, field }) => { + if (!(field in BytableFieldTypes)) { + throw new Error(`Unknown field type: ${field}`); + } + const fieldInstance = new BytableFieldTypes[field](); + fieldInstance.setName(name); + return fieldInstance; + }); + } + + getFieldValueByName(name: string) { + if (name === '') { + return undefined; + } + const field = this.fields_.find((field) => field.name === name); + if (!field) { + throw new Error(`Field ${name} not found`); + } + return field.value; + } + + setFieldValueByName(name: string, value: string | number | Buffer) { + if (name === '') { + return; + } + const field = this.fields_.find((field) => field.name === name); + if (!field) { + throw new Error(`Field ${name} not found`); + } + field.setValue(value); + } + + get name() { + return this.name_; + } + + get value(): string | number { + throw new Error('Not implemented'); + } + + setValue(_value: string | number | Buffer) { + throw new Error('Not implemented'); + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableDword.ts b/libs/@rustymotors/binary/src/lib/BytableDword.ts index e3989ee2c..0f9bf3ca8 100644 --- a/libs/@rustymotors/binary/src/lib/BytableDword.ts +++ b/libs/@rustymotors/binary/src/lib/BytableDword.ts @@ -1,39 +1,58 @@ -import { Bytable } from "./Bytable"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . -export class BytableDword extends Bytable { - private static validateBufferLength( - buffer: Buffer, - minLength: number, - offset: number = 0, - ) { - if (buffer.length < offset + minLength) { - throw new Error("Cannot deserialize buffer with insufficient length"); - } - } +import { Bytable } from './Bytable.js'; +import { BytableObject } from './types.js'; - static override fromBuffer(buffer: Buffer, offset: number) { - BytableDword.validateBufferLength(buffer, 4, offset); - const dword = new BytableDword(); - dword.deserialize(buffer.subarray(offset, offset + 4)); +export class BytableDword extends Bytable implements BytableObject { + private static validateBufferLength( + buffer: Buffer, + minLength: number, + offset = 0, + ) { + if (buffer.length < offset + minLength) { + throw new Error( + 'Cannot deserialize buffer with insufficient length', + ); + } + } - return dword; - } + static fromBuffer(buffer: Buffer, offset: number) { + BytableDword.validateBufferLength(buffer, 4, offset); + const dword = new BytableDword(); + dword.deserialize(buffer.subarray(offset, offset + 4)); - override deserialize(buffer: Buffer) { - BytableDword.validateBufferLength(buffer, 4); - super.deserialize(buffer.subarray(0, 4)); - } + return dword; + } - override get json() { - return { - name: this.name, - value: this.buffer.getUint32(0, true), - valueString: Buffer.from(this.buffer.buffer).toString("utf-8"), - serializeSize: this.serializeSize, - }; - } + override deserialize(buffer: Buffer) { + BytableDword.validateBufferLength(buffer, 4); + super.deserialize(buffer.subarray(0, 4)); + } - override toString() { - return this.buffer.toString(); - } - } + override get json() { + return { + name: this.name, + value: this.buffer.getUint32(0, true), + valueString: Buffer.from(this.buffer.buffer).toString('utf-8'), + serializeSize: this.serializeSize, + }; + } + + override toString() { + return this.buffer.toString(); + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableHeader.ts b/libs/@rustymotors/binary/src/lib/BytableHeader.ts index e6d9a9553..87e65ec48 100644 --- a/libs/@rustymotors/binary/src/lib/BytableHeader.ts +++ b/libs/@rustymotors/binary/src/lib/BytableHeader.ts @@ -1,105 +1,113 @@ -import { Bytable } from "./Bytable"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { Bytable } from './Bytable.js'; export class BytableHeader extends Bytable { - protected messageId_: number = 0; - protected messageLength_: number = 0; - protected messageVersion_: 0 | 1 = 0; - protected reserved_: number = 0; - protected checksum_: number = 0; - protected data_: Buffer = Buffer.alloc(0); - - static override fromBuffer(buffer: Buffer, offset: number) { - const header = new this(); - header.deserialize(buffer.subarray(offset)); - - return header; - } - - override get json() { - return { - name: this.name, - id: this.messageId, - len: this.messageLength, - version: this.messageVersion, - serializeSize: this.serializeSize, - }; - } - - override toString(): string { - return `Message ID: ${this.messageId}, Message Length: ${this.messageLength}, Message Version: ${this.messageVersion}`; - } - - setMessageId(messageId: number) { - this.messageId_ = messageId; - } - - setMessageLength(messageLength: number) { - this.messageLength_ = messageLength; - } - - setMessageVersion(messageVersion: 0 | 1) { - this.messageVersion_ = messageVersion; - } - - setReserved(reserved: number) { - this.reserved_ = reserved; - } - - setChecksum(checksum: number) { - this.checksum_ = checksum; - } - - get messageId() { - return this.messageId_; - } - - get messageLength() { - return this.messageLength_; - } - - get messageVersion() { - return this.messageVersion_; - } - - get reserved() { - return this.reserved_; - } - - get checksum() { - return this.checksum_; - } - - override get serializeSize() { - return this.messageVersion === 0 ? 4 : 12; - } - - override serialize() { - const buffer = Buffer.alloc(this.serializeSize); - buffer.writeUInt16BE(this.messageId, 0); - buffer.writeUInt16BE(this.messageLength, 2); - if (this.messageVersion !== 0) { - buffer.writeUInt16BE(257, 4); - buffer.writeUInt16BE(this.reserved, 6); - buffer.writeUInt32BE(this.checksum, 8); - } - return buffer; - } - - override deserialize(buffer: Buffer) { - super.deserialize(buffer); - this.setMessageId(this.getUint16(0)); - this.setMessageLength(this.getUint16(2)); - - // If the length is less than 12, there is no room for the message, so we assume version 0 - if (buffer.byteLength >= 12 && this.getUint16(4) === 257) { - this.setMessageVersion(1); - } else { - this.setMessageVersion(0); - } - - if (this.messageVersion === 1) { - this.setReserved(this.getUint16(6)); - this.setChecksum(this.getUint32(8)); - } - } + protected messageId_ = 0; + protected messageLength_ = 0; + protected messageVersion_: 0 | 1 = 0; + protected reserved_ = 0; + protected checksum_ = 0; + protected data_: Buffer = Buffer.alloc(0); + + override get json() { + return { + name: this.name, + id: this.messageId, + len: this.messageLength, + version: this.messageVersion, + serializeSize: this.serializeSize, + }; + } + + override toString(): string { + return `Message ID: ${this.messageId}, Message Length: ${this.messageLength}, Message Version: ${this.messageVersion}`; + } + + setMessageId(messageId: number) { + this.messageId_ = messageId; + } + + setMessageLength(messageLength: number) { + this.messageLength_ = messageLength; + } + + setMessageVersion(messageVersion: 0 | 1) { + this.messageVersion_ = messageVersion; + } + + setReserved(reserved: number) { + this.reserved_ = reserved; + } + + setChecksum(checksum: number) { + this.checksum_ = checksum; + } + + get messageId() { + return this.messageId_; + } + + get messageLength() { + return this.messageLength_; + } + + get messageVersion() { + return this.messageVersion_; + } + + get reserved() { + return this.reserved_; + } + + get checksum() { + return this.checksum_; + } + + override get serializeSize() { + return this.messageVersion === 0 ? 4 : 12; + } + + override serialize() { + const buffer = Buffer.alloc(this.serializeSize); + buffer.writeUInt16BE(this.messageId, 0); + buffer.writeUInt16BE(this.messageLength, 2); + if (this.messageVersion !== 0) { + buffer.writeUInt16BE(257, 4); + buffer.writeUInt16BE(this.reserved, 6); + buffer.writeUInt32BE(this.checksum, 8); + } + return buffer; + } + + override deserialize(buffer: Buffer) { + this.setMessageId(buffer.readUInt16BE(0)); + this.setMessageLength(buffer.readUInt16BE(2)); + + // If the length is less than 12, there is no room for the message, so we assume version 0 + if (buffer.byteLength >= 12 && buffer.readUInt16BE(4) === 257) { + this.setMessageVersion(1); + } else { + this.setMessageVersion(0); + } + + if (this.messageVersion === 1) { + this.setReserved(buffer.readUInt16BE(6)); + this.setChecksum(buffer.readUInt32BE(8)); + } + } } diff --git a/libs/@rustymotors/binary/src/lib/BytableMessage.test.ts b/libs/@rustymotors/binary/src/lib/BytableMessage.test.ts new file mode 100644 index 000000000..fe776b932 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableMessage.test.ts @@ -0,0 +1,196 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { describe, it, expect, beforeEach } from 'vitest'; +import { BytableStructure, BytableFieldTypes } from './BytableMessage.js'; +import { BytableObject } from './types.js'; + +class DummyField extends BytableFieldTypes.Buffer { + public override value_: Buffer = Buffer.from([0x01, 0x02, 0x03]); + override get serializeSize() { + return this.value_.length; + } + override serialize() { + return this.value_; + } + override deserialize(buffer: Buffer) { + this.value_ = buffer.subarray(0, 3); + } + override get value() { + return this.value_; + } + override setValue(val: Buffer) { + this.value_ = val; + } + override get json() { + return { + name: this.name, + value: this.value_.toString('hex'), + serializeSize: this.serializeSize, + }; + } +} + +describe('BytableStructure', () => { + let structure: BytableStructure; + + beforeEach(() => { + structure = new BytableStructure(); + structure.setSerializeOrder([ + { name: 'field1', field: 'Buffer' }, + { name: 'field2', field: 'Buffer' }, + ]); + }); + + it('should deserialize fields in order', () => { + const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); + // Patch Buffer field type to DummyField for predictable behavior + (BytableFieldTypes as any).Buffer = DummyField; + structure.deserialize(buffer); + expect(structure['fields_'].length).toBe(2); + expect(structure.getField('field1')?.value).toEqual( + Buffer.from([0x01, 0x02, 0x03]), + ); + expect(structure.getField('field2')?.value).toEqual( + Buffer.from([0x04, 0x05, 0x06]), + ); + }); + + it('should throw error for unknown field type', () => { + structure.setSerializeOrder([ + { name: 'bad', field: 'NotAType' as any }, + ]); + expect(() => structure.deserialize(Buffer.alloc(10))).toThrow( + 'Unknown field type: NotAType', + ); + }); + + it('should compute serializeSize as sum of field sizes', () => { + structure['fields_'] = [ + { serializeSize: 2 } as BytableObject, + { serializeSize: 3 } as BytableObject, + ]; + expect(structure.serializeSize).toBe(5); + }); + + it('should serialize fields in order', () => { + const field1 = new DummyField(); + field1.setName('field1'); + field1.setValue(Buffer.from([0x01, 0x02, 0x03])); + const field2 = new DummyField(); + field2.setName('field2'); + field2.setValue(Buffer.from([0x04, 0x05, 0x06])); + structure['fields_'] = [field1, field2]; + const serialized = structure.serialize(); + expect(serialized).toEqual( + Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]), + ); + }); + + it('should return correct json representation', () => { + structure['name_'] = 'TestStruct'; + structure['fields_'] = [ + { json: { name: 'a', value: 1 }, serializeSize: 1 } as any, + { json: { name: 'b', value: 2 }, serializeSize: 1 } as any, + ]; + expect(structure.json).toEqual({ + name: 'TestStruct', + serializeSize: 2, + fields: [ + { name: 'a', value: 1 }, + { name: 'b', value: 2 }, + ], + }); + }); + + it('should set and get name', () => { + structure.setName('MyStruct'); + expect(structure.name).toBe('MyStruct'); + }); + + it('should get field by name', () => { + const field = new DummyField(); + field.setName('foo'); + structure['fields_'] = [field]; + expect(structure.getField('foo')).toBe(field); + expect(structure.getField('bar')).toBeUndefined(); + }); + + it('should get field value by name', () => { + const field = new DummyField(); + field.setName('foo'); + field.setValue(Buffer.from([0x09])); + structure['fields_'] = [field]; + expect(structure.getFieldValueByName('foo')).toEqual( + Buffer.from([0x09]), + ); + }); + + it('should throw if getFieldValueByName not found', () => { + expect(() => structure.getFieldValueByName('nope')).toThrow( + 'Field nope not found', + ); + }); + + it('should return undefined for empty name in getFieldValueByName', () => { + expect(structure.getFieldValueByName('')).toBeUndefined(); + }); + + it('should set field value by name (existing field)', () => { + const field = new DummyField(); + field.setName('foo'); + structure['fields_'] = [field]; + structure.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + structure.setFieldValueByName('foo', Buffer.from([0x10])); + expect(field.value).toEqual(Buffer.from([0x10])); + }); + + it('should set field value by name (new field)', () => { + structure.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + structure.setFieldValueByName('foo', Buffer.from([0x20])); + expect(structure.getField('foo')?.value).toEqual(Buffer.from([0x20])); + }); + + it('should throw if setFieldValueByName with unknown field', () => { + structure.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + expect(() => + structure.setFieldValueByName('bar', Buffer.from([0x01])), + ).toThrow('Field bar not found in serialized format'); + }); + + it('should do nothing if setFieldValueByName called with empty name', () => { + expect(() => + structure.setFieldValueByName('', Buffer.from([0x01])), + ).not.toThrow(); + }); + + it('should throw on value getter', () => { + expect(() => structure.value).toThrow('This object is a container'); + }); + + it('should throw on setValue', () => { + expect(() => structure.setValue()).toThrow( + 'This object is a container', + ); + }); + + it('should stringify to JSON', () => { + structure['name_'] = 'Struct'; + structure['fields_'] = []; + expect(() => JSON.parse(structure.toString())).not.toThrow(); + expect(structure.toString()).toContain('"name":"Struct"'); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableMessage.ts b/libs/@rustymotors/binary/src/lib/BytableMessage.ts index a10549c89..41476a491 100644 --- a/libs/@rustymotors/binary/src/lib/BytableMessage.ts +++ b/libs/@rustymotors/binary/src/lib/BytableMessage.ts @@ -1,139 +1,449 @@ -import { Bytable } from "./Bytable"; -import { BytableContainer } from "./BytableContainer"; -import { BytableDword } from "./BytableDword"; -import { BytableHeader } from "./BytableHeader"; -import { BytableObject } from "./types"; -import { getServerLogger } from "rusty-motors-shared"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { Bytable } from './Bytable.js'; +import { BytableBase } from './BytableBase.js'; +import { BytableBuffer } from './BytableBuffer.js'; +import { BytableByte } from './BytableByte.js'; +import { BytableContainer, BytableShortContainer } from './BytableContainer.js'; +import { BytableCString } from './BytableCString.js'; +import { BytableData } from './BytableData.js'; +import { BytableDword } from './BytableDword.js'; +import { BytableHeader } from './BytableHeader.js'; +import { BytableWord } from './BytableWord.js'; +import { BytableObject } from './types.js'; +import { getServerLogger } from 'rusty-motors-logger'; + +export class BytableStructure extends BytableBase implements BytableObject { + protected fields_: Array = []; + protected serializeOrder_: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }> = []; + protected name_ = ''; + + override deserialize(buffer: Buffer) { + let offset = 0; + for (const field of this.serializeOrder_) { + if (!(field.field in BytableFieldTypes)) { + throw new Error(`Unknown field type: ${field.field}`); + } + + const fieldType = BytableFieldTypes[field.field]; + const fieldInstance = new fieldType(); + fieldInstance.setName(field.name); + fieldInstance.deserialize(buffer.subarray(offset)); + this.fields_.push(fieldInstance); + offset += fieldInstance.serializeSize; + } + } + + override get serializeSize() { + const fieldSizes = this.fields_.map((field) => field.serializeSize); + return fieldSizes.reduce((a, b) => a + b, 0); + } + + override serialize() { + const buffer = Buffer.alloc(this.serializeSize); + let offset = 0; + for (const field of this.fields_) { + const fieldBuffer = field.serialize(); + buffer.set(fieldBuffer, offset); + offset += field.serializeSize; + } + return buffer; + } + + get json() { + return { + name: this.name_, + serializeSize: this.serializeSize, + fields: this.fields_.map((field) => field.json), + }; + } + + setName(name: string) { + this.name_ = name; + } + + override toString() { + return JSON.stringify(this.json); + } + + setSerializeOrder( + serializeOrder: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }>, + ) { + this.serializeOrder_ = serializeOrder; + } + + getField(name: string) { + return this.fields_.find((field) => field.name === name); + } + + getFieldValueByName(name: string) { + if (name === '') { + return undefined; + } + const field = this.fields_.find((field) => field.name === name); + if (!field) { + throw new Error(`Field ${name} not found`); + } + return field.value; + } + + setFieldValueByName(name: string, value: string | number | Buffer) { + if (name === '') { + return; + } + + let coercedValue; + + if (typeof value === 'string') { + coercedValue = Buffer.from(value); + } else { + coercedValue = + typeof value === 'number' ? Buffer.from([value]) : value; + } + + const serializedFormat = this.serializeOrder_.find( + (field) => field.name === name, + ); + + if (!serializedFormat) { + throw new Error(`Field ${name} not found in serialized format`); + } + + const field = this.fields_.find((field) => field.name === name); + if (!field) { + const field = new BytableFieldTypes[serializedFormat.field](); + field.setName(name); + field.setValue(coercedValue); + + this.fields_.push(field); + return; + } + + field.setValue(value); + } + + get name(): string { + return this.name_; + } + + get value(): string | number | Buffer { + throw new Error('This object is a container'); + } + + setValue() { + throw new Error('This object is a container'); + } +} export const BytableFieldTypes = { - ZeroTerminatedString: BytableContainer, - Dword: BytableDword, - Container: BytableContainer, - Raw: Bytable, + ZeroTerminatedString: BytableContainer, + String: BytableContainer, + Dword: BytableDword, + Container: BytableContainer, + PrefixedString2: BytableShortContainer, + Raw: BytableData, + Structure: BytableStructure, + Boolean: BytableByte, + Short: BytableWord, + Buffer: BytableBuffer, + CString: BytableCString, }; export class BytableMessage extends Bytable { - protected header_: BytableHeader = new BytableHeader(); - protected fields_: Array = []; - protected serializeOrder_: Array<{ - name: string; - field: keyof typeof BytableFieldTypes; - }> = []; - - constructor(version: 0 | 1 = 1) { - super(); - this.header_.setMessageVersion(version); - } - - static override fromBuffer(buffer: Buffer, offset: number) { - const message = new this(1); - message.deserialize(buffer.subarray(offset)); - return message; - } - - override deserialize(buffer: Buffer) { - try { - const header = BytableHeader.fromBuffer(buffer, this.header_.messageVersion); - this.header_.deserialize(buffer.subarray(0, header.serializeSize)); - let offset = header.serializeSize; - - for (const field of this.serializeOrder_) { - if (!(field.field in BytableFieldTypes)) { - throw new Error(`Unknown field type: ${field.field}`); - } - - const fieldType = BytableFieldTypes[field.field]; - const fieldInstance = fieldType.fromBuffer(buffer, offset); - fieldInstance.setName(field.name); - this.fields_.push(fieldInstance); - offset += fieldInstance.serializeSize; - } - } catch (error) { - const err = new Error( - `Error deserializing message: ${(error as Error).message}`, - { - cause: error, - }, - ); - throw err; - } - } - - get header() { - return this.header_; - } - - override get serializeSize() { - const fieldSizes = this.fields_.map((field) => field.serializeSize); - return this.header_.serializeSize + fieldSizes.reduce((a, b) => a + b, 0); - } - - override serialize() { - const buffer = Buffer.alloc(this.serializeSize); - buffer.set(this.header_.serialize(), 0); - let offset = this.header_.serializeSize; - - // It's posible that this message is only a header - if ( this.header_.messageVersion === 0 && this.header_.messageLength === 4) { - getServerLogger().warn(`Message has no fields, or no body`); - return buffer; - } - - for (const field of this.fields_) { - buffer.set(field.serialize(), offset); - offset += field.serializeSize; - } - return buffer; - } - - override get json() { - return { - name: this.name, - serializeSize: this.serializeSize, - header: this.header_.json, - fields: this.fields_.map((field) => field.json), - }; - } - - override setName(name: string) { - this.header_.setName(name); - } - - override toString() { - return JSON.stringify(this.json); - } - - setSerializeOrder( - serializeOrder: Array<{ - name: string; - field: keyof typeof BytableFieldTypes; - }>, - ) { - this.serializeOrder_ = serializeOrder; - } - - getFieldValueByName(name: string) { - if (name === "") { - return undefined; - } - const field = this.fields_.find((field) => field.name === name); - if (!field) { - throw new Error(`Field ${name} not found`); - } - return field.value; - } - - setFieldValueByName(name: string, value: string | number | Buffer) { - if (name === "") { - return; - } - const field = this.fields_.find((field) => field.name === name); - if (!field) { - throw new Error(`Field ${name} not found`); - } - field.setValue(value); - } - - setVersion(version: 0 | 1) { - this.header_.setMessageVersion(version); - } + protected header_: BytableHeader = new BytableHeader(); + protected fields_: Array = []; + protected serializeOrder_: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }> = []; + + constructor(version: 0 | 1 = 1) { + super(); + this.header_.setMessageVersion(version); + this.setSerializeOrder([ + { + name: 'data', + field: 'Buffer', + }, + ]); + } + + protected override deserializeFields(buffer: Buffer) { + let offset = 0; + + // It's posible that this message is only a header + if ( + this.header_.messageVersion === 0 && + this.header_.messageLength === 4 + ) { + this.fields_.push(new BytableFieldTypes.Buffer()); + } + + for (const field of this.serializeOrder_) { + if (!(field.field in BytableFieldTypes)) { + throw new Error(`Unknown field type: ${field.field}`); + } + + const fieldType = BytableFieldTypes[field.field]; + const fieldInstance = new fieldType(); + fieldInstance.setName(field.name); + + try { + fieldInstance.deserialize(buffer.subarray(offset)); + } catch (error) { + const err = new Error( + `Error deserializing field ${field.name} ${error}`, + { + cause: error, + }, + ); + getServerLogger('BytableMessage/deserializeFields').error( + String(err), + ); + throw err; + } + this.fields_.push(fieldInstance); + offset += fieldInstance.serializeSize; + } + } + + override deserialize(buffer: Buffer) { + try { + this.header_.deserialize(buffer); + this.deserializeFields(buffer.subarray(this.header.serializeSize)); + } catch (error) { + const err = new Error(`Error deserializing message ${error}`, { + cause: error, + }); + throw err; + } + } + + get header() { + return this.header_; + } + + override get serializeSize() { + const fieldSizes = this.fields_.map((field) => field.serializeSize); + return ( + this.header_.serializeSize + fieldSizes.reduce((a, b) => a + b, 0) + ); + // return this.align8(this.header_.serializeSize + fieldSizes.reduce((a, b) => a + b, 0)); + } + + protected override serializeFields() { + const buffer = Buffer.alloc( + this.serializeSize - this.header_.serializeSize, + ); + let offset = 0; + + // It's posible that this message is only a header + if ( + this.header_.messageVersion === 0 && + this.header_.messageLength === 4 + ) { + this.fields_.push(new BytableFieldTypes.Buffer()); + } + + for (const field of this.fields_) { + try { + buffer.set(field.serialize(), offset); + } catch (error) { + const err = new Error(`Error serializing field ${field.name}`, { + cause: error, + }); + throw err; + } + offset += field.serializeSize; + } + return buffer; + } + + override serialize() { + const buffer = Buffer.alloc(this.serializeSize); + this.header_.setMessageLength(this.serializeSize); + buffer.set(this.header_.serialize(), 0); + + buffer.set(this.serializeFields(), this.header_.serializeSize); + + return buffer; + } + + override get json() { + return { + name: this.name, + serializeSize: this.serializeSize, + header: this.header_.json, + fields: this.fields_.map((field) => field.json), + }; + } + + override setName(name: string) { + this.header_.setName(name); + } + + override toString() { + return JSON.stringify(this.json); + } + + setSerializeOrder( + serializeOrder: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }>, + ) { + this.serializeOrder_ = serializeOrder; + } + + getField(name: string) { + return this.fields_.find((field) => field.name === name); + } + + getFieldValueByName(name: string) { + if (name === '') { + return undefined; + } + const field = this.fields_.find((field) => field.name === name); + if (!field) { + throw new Error(`Field ${name} not found`); + } + return field.value; + } + + htonl(value: number) { + const buffer = Buffer.alloc(4); + buffer.writeUInt32BE(value, 0); + return buffer; + } + + coerceValue(value: string | number | Buffer) { + if (typeof value === 'string') { + return Buffer.from(value); + } + if (typeof value === 'number') { + return Buffer.from(this.htonl(value)); + } + return value; + } + + setFieldValueByName(name: string, value: string | number | Buffer) { + if (name === '') { + return; + } + + const serializedFormat = this.serializeOrder_.find( + (field) => field.name === name, + ); + + if (!serializedFormat) { + throw new Error(`Field ${name} not found in serialized format`); + } + + const field = this.fields_.find((field) => field.name === name); + if (!field) { + const field = new BytableFieldTypes[serializedFormat.field](); + field.setName(name); + field.setValue(this.coerceValue(value)); + + this.fields_.push(field); + return; + } + + field.setValue(value); + } + + setVersion(version: 0 | 1) { + this.header_.setMessageVersion(version); + } + + getBody() { + return this.serializeFields(); + } + + setBody(buffer: Buffer) { + this.deserializeFields(buffer); + } + + get data() { + return this.getBody(); + } + + set data(buffer: Buffer) { + this.setBody(buffer); + } + + toHexString() { + return this.serialize().toString('hex'); + } + + getByteSize() { + return this.serializeSize; + } +} + +/** + * Creates a version 0 {@link BytableMessage} with a single 'data' field of type Buffer. + * + * If a buffer is provided, the message is deserialized from it. + * + * @param buffer - Optional buffer to deserialize into the message. + * @returns A {@link BytableMessage} instance configured for raw data. + */ +export function createRawMessage(buffer?: Buffer) { + const message = new BytableMessage(0); + message.setSerializeOrder([ + { + name: 'data', + field: 'Buffer', + }, + ]); + + if (buffer) { + message.deserialize(buffer); + } + + return message; +} + +/** + * Creates a version 1 `BytableMessage` with a single `data` field of type `Buffer`. + * + * If a buffer is provided, the message is deserialized from it. + * + * @param buffer - Optional buffer to deserialize into the message. + * @returns A `BytableMessage` instance configured for game messages. + */ +export function createGameMessage(buffer?: Buffer) { + const message = new BytableMessage(1); + message.setSerializeOrder([ + { + name: 'data', + field: 'Buffer', + }, + ]); + + if (buffer) { + message.deserialize(buffer); + } + + return message; } diff --git a/libs/@rustymotors/binary/src/lib/BytableServerHeader.test.ts b/libs/@rustymotors/binary/src/lib/BytableServerHeader.test.ts new file mode 100644 index 000000000..c1f8d2ef8 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableServerHeader.test.ts @@ -0,0 +1,122 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { describe, it, expect, beforeEach } from 'vitest'; +import { BytableServerHeader } from './BytableServerHeader.js'; + +// Mock BytableBase since it's not provided +class MockBytableBase { + deserialize(_buffer: Buffer) { + // Mock implementation + } +} +Object.setPrototypeOf(BytableServerHeader.prototype, MockBytableBase.prototype); + +describe('BytableServerHeader', () => { + let header: BytableServerHeader; + + beforeEach(() => { + header = new BytableServerHeader(); + }); + + it('should have default values', () => { + expect(header.name).toBe('ServerHeader'); + expect(header.messageLength).toBe(0); + expect(header.sequence).toBe(0); + expect(header.flags).toBe(0); + expect(header['messageSignature_']).toBe('TOMC'); + expect(header.serializeSize).toBe(11); + }); + + it('should throw on setName', () => { + expect(() => header.setName('foo')).toThrow('Method not implemented.'); + }); + + it('should throw on get value', () => { + expect(() => header.value).toThrow('Method not implemented.'); + }); + + it('should throw on setValue', () => { + expect(() => header.setValue(123)).toThrow('Method not implemented.'); + }); + + it('should return correct json', () => { + header.setMessageLength(42); + header.sequence = 1234; + header.flags = 7; + const json = header.json; + expect(json).toEqual({ + name: 'ServerHeader', + len: 42, + signature: 'TOMC', + sequence: 1234, + flags: 7, + serializeSize: 11, + }); + }); + + it('should return correct toString', () => { + header.setMessageLength(10); + header.sequence = 99; + header.flags = 1; + expect(header.toString()).toBe( + 'Message Length: 10, Message Signature: TOMC, Message Sequence: 99, Message Flags: 1', + ); + }); + + it('should set and get messageLength', () => { + header.setMessageLength(256); + expect(header.messageLength).toBe(256); + }); + + it('should set and get sequence', () => { + header.sequence = 555; + expect(header.sequence).toBe(555); + }); + + it('should set and get flags', () => { + header.flags = 3; + expect(header.flags).toBe(3); + }); + + it('should serialize to buffer correctly', () => { + header.setMessageLength(0x1234); + header.sequence = 0x87654321; + header.flags = 0xab; + const buf = header.serialize(); + expect(buf).toBeInstanceOf(Buffer); + expect(buf.length).toBe(11); + expect(buf.readUInt16LE(0)).toBe(0x1234); + expect(buf.toString('utf8', 2, 6)).toBe('TOMC'); + expect(buf.readUInt32LE(6)).toBe(0x87654321); + expect(buf.readUInt8(10)).toBe(0xab); + }); + + it('should deserialize from buffer correctly', () => { + const buf = Buffer.alloc(11); + buf.writeUInt16LE(0x4321, 0); + buf.write('TOMC', 2); + buf.writeUInt32LE(0x12345678, 6); + buf.writeUInt8(0xcd, 10); + + header.deserialize(buf); + + expect(header.messageLength).toBe(0x4321); + expect(header['messageSignature_']).toBe('TOMC'); + expect(header.sequence).toBe(0x12345678); + expect(header.flags).toBe(0xcd); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableServerHeader.ts b/libs/@rustymotors/binary/src/lib/BytableServerHeader.ts new file mode 100644 index 000000000..447a37c9f --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableServerHeader.ts @@ -0,0 +1,116 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableBase } from './BytableBase.js'; +import { BytableObject } from './types.js'; + +/** + * Represents a server message header that can be serialized and deserialized to and from a binary format. + * + * The header contains the following fields (all in Little Endian): + * - `messageLength_` (2 bytes): The length of the message. + * - `messageSignature_` (4 bytes): A fixed signature string, defaulting to 'TOMC'. + * - `messageSequence_` (4 bytes): The sequence number of the message. + * - `messageFlags_` (1 byte): A bitfield representing message flags. + * + * Provides methods for serialization, deserialization, and JSON representation. + * + * @extends BytableBase + * @implements BytableObject + */ +export class BytableServerHeader extends BytableBase implements BytableObject { + // All fields are in Little Endian + protected messageLength_ = 0; // 2 bytes + protected messageSignature_ = 'TOMC'; // 4 bytes + protected messageSequence_ = 0; // 4 bytes + protected messageFlags_ = 0; // 1 byte bitfield + + get name(): string { + return 'ServerHeader'; + } + + setName(_name: string): void { + throw new Error('Method not implemented.'); + } + + get value(): string | number | Buffer { + throw new Error('Method not implemented.'); + } + + setValue(_value: string | number | Buffer): void { + throw new Error('Method not implemented.'); + } + + get json() { + return { + name: this.name, + len: this.messageLength, + signature: this.messageSignature_, + sequence: this.messageSequence_, + flags: this.messageFlags_, + serializeSize: this.serializeSize, + }; + } + + override toString(): string { + return `Message Length: ${this.messageLength_}, Message Signature: ${this.messageSignature_}, Message Sequence: ${this.messageSequence_}, Message Flags: ${this.messageFlags_}`; + } + + setMessageLength(messageLength: number) { + this.messageLength_ = messageLength; + } + + get messageLength() { + return this.messageLength_; + } + + override get serializeSize() { + return 11; + } + + override serialize() { + const buffer = Buffer.alloc(this.serializeSize); + buffer.writeUInt16LE(this.messageLength, 0); + buffer.write(this.messageSignature_, 2); + buffer.writeUInt32LE(this.messageSequence_, 6); + buffer.writeUInt8(this.messageFlags_, 10); + return buffer; + } + + override deserialize(buffer: Buffer) { + super.deserialize(buffer); + this.messageLength_ = buffer.readUInt16LE(0); + this.messageSignature_ = buffer.toString('utf8', 2, 6); + this.messageSequence_ = buffer.readUInt32LE(6); + this.messageFlags_ = buffer.readUInt8(10); + } + + get sequence() { + return this.messageSequence_; + } + + get flags() { + return this.messageFlags_; + } + + set sequence(sequence: number) { + this.messageSequence_ = sequence; + } + + set flags(flags: number) { + this.messageFlags_ = flags; + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableServerMessage.test.ts b/libs/@rustymotors/binary/src/lib/BytableServerMessage.test.ts new file mode 100644 index 000000000..3c4e36901 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableServerMessage.test.ts @@ -0,0 +1,291 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Patch dependencies with inline mocks (must be at the very top for Vitest hoisting) +vi.mock('./BytableServerHeader.js', () => ({ + BytableServerHeader: class { + static deserializeCalled = false; + static serializeCalled = false; + serializeSize = 6; + sequence = 0; + flags = 0; + name = ''; + setName(name: string) { + this.name = name; + } + setMessageLength() { + // Mock implementation + } + deserialize() { + (this.constructor as any).deserializeCalled = true; + } + serialize() { + (this.constructor as any).serializeCalled = true; + return Buffer.alloc(6); + } + get json() { + return { + name: this.name, + sequence: this.sequence, + flags: this.flags, + }; + } + }, +})); +vi.mock('./BytableMessage.js', () => ({ + BytableFieldTypes: { + Buffer: class { + name = ''; + value: any = null; + serializeSize = 4; + static deserializeCalled = false; + static serializeCalled = false; + setName(name: string) { + this.name = name; + } + setValue(value: any) { + this.value = value; + } + deserialize(buffer: Buffer) { + (this.constructor as any).deserializeCalled = true; + this.value = buffer.readUInt32BE(0); + } + serialize() { + (this.constructor as any).serializeCalled = true; + const buf = Buffer.alloc(4); + buf.writeUInt32BE( + typeof this.value === 'number' ? this.value : 0, + 0, + ); + return buf; + } + get json() { + return { name: this.name, value: this.value }; + } + }, + UInt32: class { + name = ''; + value: any = null; + serializeSize = 4; + static deserializeCalled = false; + static serializeCalled = false; + setName(name: string) { + this.name = name; + } + setValue(value: any) { + this.value = value; + } + deserialize(buffer: Buffer) { + (this.constructor as any).deserializeCalled = true; + this.value = buffer.readUInt32BE(0); + } + serialize() { + (this.constructor as any).serializeCalled = true; + const buf = Buffer.alloc(4); + buf.writeUInt32BE( + typeof this.value === 'number' ? this.value : 0, + 0, + ); + return buf; + } + get json() { + return { name: this.name, value: this.value }; + } + }, + }, +})); + +// Define mock class for use in test body +class MockHeader { + static deserializeCalled = false; + static serializeCalled = false; + serializeSize = 6; + sequence = 0; + flags = 0; + name = ''; + setName(name: string) { + this.name = name; + } + setMessageLength() { + // Mock implementation + } + deserialize() { + (this.constructor as any).deserializeCalled = true; + } + serialize() { + (this.constructor as any).serializeCalled = true; + return Buffer.alloc(6); + } + get json() { + return { name: this.name, sequence: this.sequence, flags: this.flags }; + } +} + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { BytableServerMessage } from './BytableServerMessage.js'; + +const setupMsg = ( + serializeOrder = [{ name: 'foo', field: 'Buffer' as any }], + headerClass: any = MockHeader, +) => { + const msg = new BytableServerMessage(); + // @ts-ignore + msg.header_ = new headerClass(); + msg.setSerializeOrder(serializeOrder); + return msg; +}; + +describe('BytableServerMessage', () => { + let msg: BytableServerMessage; + beforeEach(() => { + msg = setupMsg(); + // @ts-ignore + msg.fields_ = []; + // @ts-ignore + msg.serializeOrder_ = []; + }); + + it('should initialize with default header and fields', () => { + // @ts-ignore + expect(msg.header_).toBeInstanceOf(MockHeader); + // @ts-ignore + expect(msg.fields_).toEqual([]); + }); + + it('should set and get serializeOrder', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + // @ts-ignore + expect(msg.serializeOrder_).toEqual([{ name: 'foo', field: 'Buffer' }]); + }); + + it('should set and get sequenceNumber', () => { + msg.setSequenceNumber(42); + expect(msg.sequenceNumber).toBe(42); + }); + + it('should set and get flags', () => { + msg.setFlags(7); + expect(msg.flags).toBe(7); + }); + + it('should set and get name via setName', () => { + msg.setName('TestMsg'); + expect(msg.header.name).toBe('TestMsg'); + }); + + it('should throw if getFieldValueByName not found', () => { + expect(() => msg.getFieldValueByName('notfound')).toThrow( + /Field notfound not found/, + ); + }); + + it('should return undefined for getFieldValueByName with empty name', () => { + expect(msg.getFieldValueByName('')).toBeUndefined(); + }); + + it('should set and get field value by name', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1234); + expect(msg.getFieldValueByName('foo')).toBeInstanceOf(Buffer); + }); + + it('should throw if setFieldValueByName with unknown field', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + expect(() => msg.setFieldValueByName('bar', 1)).toThrow( + /Field bar not found in serialized format/, + ); + }); + + it('should serialize and deserialize fields', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 42); + const buf = msg.serialize(); + expect(Buffer.isBuffer(buf)).toBe(true); + const msg2 = setupMsg(); + msg2.deserialize(buf); + expect(msg2.getFieldValueByName('foo')).toBeDefined(); + }); + + it('should return correct serializeSize', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1); + expect(msg.serializeSize).toBe(6 + 4); + }); + + it('should return correct json', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1); + const json = msg.json; + expect(json).toHaveProperty('name'); + expect(json).toHaveProperty('serializeSize'); + expect(json).toHaveProperty('header'); + expect(json).toHaveProperty('fields'); + }); + + it('should stringify to JSON', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1); + expect(() => JSON.parse(msg.toString())).not.toThrow(); + }); + + it('should get and set body', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1); + const body = msg.getBody(); + expect(Buffer.isBuffer(body)).toBe(true); + const msg2 = setupMsg(); + msg2.setBody(body); + expect(msg2.getFieldValueByName('foo')).toBeDefined(); + }); + + it('should get and set data property', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1); + const data = msg.data; + expect(Buffer.isBuffer(data)).toBe(true); + const msg2 = setupMsg(); + msg2.data = data; + expect(msg2.getFieldValueByName('foo')).toBeDefined(); + }); + + it('should return hex string', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'Buffer' }]); + msg.setFieldValueByName('foo', 1); + const hex = msg.toHexString(); + expect(typeof hex).toBe('string'); + expect(hex).toMatch(/^[0-9a-f]*$/); + }); + + it('should throw on unknown field type in deserializeFields', () => { + msg.setSerializeOrder([{ name: 'foo', field: 'UnknownType' as any }]); + expect(() => { + // @ts-ignore + msg.deserializeFields(Buffer.alloc(10)); + }).toThrow(/Unknown field type/); + }); + + it('should throw on error in deserialize', () => { + class BadHeader extends MockHeader { + override deserialize() { + throw new Error('bad header'); + } + } + const badMsg = setupMsg([{ name: 'foo', field: 'Buffer' }], BadHeader); + expect(() => badMsg.deserialize(Buffer.alloc(10))).toThrow( + /Error deserializing message/, + ); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/BytableServerMessage.ts b/libs/@rustymotors/binary/src/lib/BytableServerMessage.ts new file mode 100644 index 000000000..e2191b745 --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableServerMessage.ts @@ -0,0 +1,215 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { Bytable } from './Bytable.js'; +import { BytableFieldTypes } from './BytableMessage.js'; +import { BytableServerHeader } from './BytableServerHeader.js'; +import { BytableObject } from './types.js'; + +export class BytableServerMessage extends Bytable { + protected header_: BytableServerHeader = new BytableServerHeader(); + protected fields_: Array = []; + protected serializeOrder_: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }> = []; + + constructor() { + super(); + } + + protected override deserializeFields(buffer: Buffer) { + let offset = 0; + // Clear fields before deserializing + this.fields_ = []; + + // Only set default serializeOrder_ if it is empty + if (this.serializeOrder_.length === 0) { + this.setSerializeOrder([ + { + name: 'data', + field: 'Buffer', + }, + ]); + } + + for (const field of this.serializeOrder_) { + if (!(field.field in BytableFieldTypes)) { + throw new Error(`Unknown field type: ${field.field}`); + } + + const fieldType = BytableFieldTypes[field.field]; + const fieldInstance = new fieldType(); + fieldInstance.setName(field.name); + fieldInstance.deserialize(buffer.subarray(offset)); + this.fields_.push(fieldInstance); + offset += fieldInstance.serializeSize; + } + } + + override deserialize(buffer: Buffer) { + try { + // Use the current header_ instance (may be a mock) + const header = this.header_; + header.deserialize(buffer.subarray(0, header.serializeSize)); + this.deserializeFields(buffer.subarray(header.serializeSize)); + } catch (error) { + const err = new Error( + `Error deserializing message: ${(error as Error).message}`, + { + cause: error, + }, + ); + throw err; + } + } + + get header() { + return this.header_; + } + + override get serializeSize() { + const fieldSizes = this.fields_.map((field) => field.serializeSize); + return ( + this.header_.serializeSize + fieldSizes.reduce((a, b) => a + b, 0) + ); + // return this.align8(this.header_.serializeSize + fieldSizes.reduce((a, b) => a + b, 0)); + } + + protected override serializeFields() { + const buffer = Buffer.alloc( + this.serializeSize - this.header_.serializeSize, + ); + let offset = 0; + + for (const field of this.fields_) { + buffer.set(field.serialize(), offset); + offset += field.serializeSize; + } + return buffer; + } + + override serialize() { + const buffer = Buffer.alloc(this.serializeSize); + this.header_.setMessageLength(this.serializeSize - 2); + buffer.set(this.header_.serialize(), 0); + + buffer.set(this.serializeFields(), this.header_.serializeSize); + + return buffer; + } + + override get json() { + return { + name: this.name, + serializeSize: this.serializeSize, + header: this.header_.json, + fields: this.fields_.map((field) => field.json), + }; + } + + override setName(name: string) { + this.header_.setName(name); + } + + override toString() { + return JSON.stringify(this.json); + } + + setSerializeOrder( + serializeOrder: Array<{ + name: string; + field: keyof typeof BytableFieldTypes; + }>, + ) { + this.serializeOrder_ = serializeOrder; + } + + get sequenceNumber() { + return this.header_.sequence; + } + + setSequenceNumber(sequence: number) { + this.header_.sequence = sequence; + } + + get flags() { + return this.header_.flags; + } + + setFlags(flags: number) { + this.header_.flags = flags; + } + + getFieldValueByName(name: string) { + if (name === '') { + return undefined; + } + const field = this.fields_.find((field) => field.name === name); + if (!field) { + throw new Error(`Field ${name} not found`); + } + return field.value; + } + + setFieldValueByName(name: string, value: string | number | Buffer) { + if (name === '') { + return; + } + + const serializedFormat = this.serializeOrder_.find( + (field) => field.name === name, + ); + + if (!serializedFormat) { + throw new Error(`Field ${name} not found in serialized format`); + } + + const field = this.fields_.find((field) => field.name === name); + if (!field) { + const field = new BytableFieldTypes[serializedFormat.field](); + field.setName(name); + field.setValue(Buffer.from(this.toBuffer(value))); + + this.fields_.push(field); + return; + } + + field.setValue(value); + } + + getBody() { + return this.serializeFields(); + } + + setBody(buffer: Buffer) { + // Clear fields before setting body + this.fields_ = []; + this.deserializeFields(buffer); + } + + get data() { + return this.getBody(); + } + + set data(buffer: Buffer) { + this.setBody(buffer); + } + + toHexString() { + return this.serialize().toString('hex'); + } +} diff --git a/libs/@rustymotors/binary/src/lib/BytableWord.ts b/libs/@rustymotors/binary/src/lib/BytableWord.ts new file mode 100644 index 000000000..4178530cb --- /dev/null +++ b/libs/@rustymotors/binary/src/lib/BytableWord.ts @@ -0,0 +1,53 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { Bytable } from './Bytable.js'; + +export class BytableWord extends Bytable { + private static validateBufferLength( + buffer: Buffer, + minLength: number, + offset = 0, + ) { + if (buffer.length < offset + minLength) { + throw new Error( + 'Cannot deserialize buffer with insufficient length', + ); + } + } + + override deserialize(buffer: Buffer) { + BytableWord.validateBufferLength(buffer, 2); + super.deserialize(buffer.subarray(0, 2)); + } + + override get json() { + return { + name: this.name, + value: this.buffer.getUint16(0, true), + valueString: Buffer.from(this.buffer.buffer).toString('utf-8'), + serializeSize: this.serializeSize, + }; + } + + override toString() { + return this.buffer.toString(); + } + + override get serializeSize(): number { + return 2; + } +} diff --git a/libs/@rustymotors/binary/src/lib/binary.test.ts b/libs/@rustymotors/binary/src/lib/binary.test.ts index b999e3b2f..effdd644c 100644 --- a/libs/@rustymotors/binary/src/lib/binary.test.ts +++ b/libs/@rustymotors/binary/src/lib/binary.test.ts @@ -1,175 +1,200 @@ -import { describe, it, expect, beforeEach } from "vitest"; -import { BytableMessage } from "./BytableMessage"; -import { BytableHeader } from "./BytableHeader"; -import { BytableContainer } from "./BytableContainer"; -import { BytableDword } from "./BytableDword"; -import { BytableBase } from "./BytableBase"; - -describe("Binary Message Parsing", () => { - const testHex = - "0501013e010100000000013e0022643331366364326464366266383730383933646662616166313766393635383834650000010032323841413331423743423530463233343933323539323730413842334430353233333534463146323936413132343430444541343942353031453843363346443841394141453143463534304242333535354245363437324641423230443632303634304239394645453837414139373332363938304231303430323341314132354233453635314534413434453445394242313341453644303033383233303544434336393035434135393032363435443332463138413644414434443334363937304531443931313944424534363746373333364443334333444439413043443942463741313945453331323631443945444146413139344445443846000432313736fea31c19"; - const testBuffer = Buffer.from(testHex, "hex"); - - describe("BytableBase", () => { - it("should throw error when deserializing empty buffer", () => { +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { describe, it, expect, beforeEach } from 'vitest'; +import { BytableMessage } from './BytableMessage.js'; +import { BytableHeader } from './BytableHeader.js'; +import { BytableContainer } from './BytableContainer.js'; +import { BytableDword } from './BytableDword.js'; +import { BytableBase } from './BytableBase.js'; + +describe('Binary Message Parsing', () => { + const testHex = + '0501013e010100000000013e0022643331366364326464366266383730383933646662616166313766393635383834650000010032323841413331423743423530463233343933323539323730413842334430353233333534463146323936413132343430444541343942353031453843363346443841394141453143463534304242333535354245363437324641423230443632303634304239394645453837414139373332363938304231303430323341314132354233453635314534413434453445394242313341453644303033383233303544434336393035434135393032363435443332463138413644414434443334363937304531443931313944424534363746373333364443334333444439413043443942463741313945453331323631443945444146413139344445443846000432313736fea31c19'; + const testBuffer = Buffer.from(testHex, 'hex'); + + describe('BytableBase', () => { + it('should throw error when called directly', () => { const base = new BytableBase(); - expect(() => base.deserialize(Buffer.alloc(0))).toThrowError("Cannot deserialize empty buffer"); + expect(() => base.deserialize(Buffer.alloc(0))).toThrowError( + 'Method should be implemented by subclass', + ); }); }); - describe("BytableHeader", () => { - it("should correctly parse message header", () => { - const header = BytableHeader.fromBuffer(testBuffer, 0); - - expect(header.messageId).toBe(0x0501); - expect(header.messageLength).toBe(0x013e); - expect(header.messageVersion).toBe(1); - expect(header.serializeSize).toBe(12); // Version 1 header size - }); - }); - - describe("BytableMessage", () => { - let message: BytableMessage; - - beforeEach(() => { - message = BytableMessage.fromBuffer(testBuffer, 0); - message.setSerializeOrder([ - { name: "ContextId", field: "Container" }, - { name: "", field: "Container" }, - { name: "SessionKey", field: "Container" }, - { name: "GameId", field: "Container" }, - { name: "", field: "Dword" }, - ]); - message.deserialize(testBuffer); - }); - - it("should correctly parse the full message", () => { - expect(message.header.messageId).toBe(0x0501); - expect(message.header.messageVersion).toBe(1); - }); - - it("should retrieve field values by name", () => { - const sessionKey = message.getFieldValueByName("SessionKey"); - expect(sessionKey).toBe( - "228AA31B7CB50F23493259270A8B3D0523354F1F296A12440DEA49B501E8C63FD8A9AAE1CF540BB3555BE6472FAB20D620640B99FEE87AA97326980B104023A1A25B3E651E4A44E4E9BB13AE6D00382305DCC6905CA5902645D32F18A6DAD4D346970E1D9119DBE467F7336DC3C3DD9A0CD9BF7A19EE31261D9EDAFA194DED8F", - ); - }); - - it("should serialize back to the original hex string", () => { - const serialized = message.serialize(); - expect(serialized.toString("hex")).toBe(testHex); - }); - - it("should throw error when accessing non-existent field", () => { - expect(() => { - message.getFieldValueByName("NonExistentField"); - }).toThrowError("Field NonExistentField not found"); - }); - }); - - describe("BytableContainer", () => { - describe("Non-null-terminated mode", () => { - let container: BytableContainer; - - beforeEach(() => { - container = new BytableContainer(); - container.setNullTerminated(false); - }); - - it("should dynamically adjust length to match content", () => { - container.setValue("hello"); - expect(container.getLength()).toBe(5); - expect(container.getValue()).toBe("hello"); - - container.setValue("longer string"); - expect(container.getLength()).toBe(13); - expect(container.getValue()).toBe("longer string"); - }); - - it("should serialize with length prefix", () => { - container.setValue("test"); - const serialized = container.serialize(); - // First 2 bytes should be length (4), followed by "test" - expect(serialized.length).toBe(6); // 2 bytes length + 4 bytes content - expect(serialized.readUInt16BE(0)).toBe(4); // Length prefix - expect(serialized.subarray(2).toString()).toBe("test"); - }); - - it("should calculate correct serializeSize", () => { - container.setValue("hello"); - expect(container.serializeSize).toBe(7); // 5 (content) + 2 (length prefix) - }); - }); - - describe("Null-terminated mode", () => { - let container: BytableContainer; - - beforeEach(() => { - container = new BytableContainer(); - container.setNullTerminated(true); - }); - - it("should return a single null terminator when serializing empty string", () => { - container.setNullTerminated(true); - const serialized = container.serialize(); - expect(serialized.toString("hex")).toBe("00"); - }); - - it("should serialize without length prefix", () => { - container.setNullTerminated(true); - container.setValue("test"); - const serialized = container.serialize(); - expect(serialized.toString("hex")).toBe("7465737400"); - }); - - it("should calculate correct serializeSize", () => { - container.setNullTerminated(true); - container.setValue("hello"); - expect(container.serializeSize).toBe(6); // Just the content length (including null terminator) - }); - - it("should throw when setting length for null-terminated container", () => { - container.setNullTerminated(true); - - // Can't change length after setting value - expect(() => container.setLength(5)).toThrowError( - "Cannot set length for null terminated container", - ); - }); - }); - - describe("Edge cases", () => { - let container: BytableContainer; - - beforeEach(() => { - container = new BytableContainer(); - }); - - it("should handle empty strings", () => { - container.setValue(""); - expect(container.getLength()).toBe(0); - expect(container.getValue()).toBe(""); - }); - - it("should throw error when setting empty buffer in null-terminated mode", () => { - container.setNullTerminated(true); - expect(() => container.setValue(Buffer.alloc(0))).toThrowError( - "Cannot set empty buffer", - ); - }); - }); - }); - - describe("BytableDword", () => { - it("should throw error when deserializing buffer with insufficient length", () => { + describe('BytableHeader', () => { + it('should correctly parse message header', () => { + const header = new BytableHeader(); + header.setMessageVersion(1); + header.deserialize(testBuffer); + + expect(header.messageId).toBe(0x0501); + expect(header.messageLength).toBe(0x013e); + expect(header.messageVersion).toBe(1); + expect(header.serializeSize).toBe(12); // Version 1 header size + }); + }); + + describe('BytableMessage', () => { + let message: BytableMessage; + + beforeEach(() => { + message = new BytableMessage(); + message.setSerializeOrder([ + { name: 'ContextId', field: 'PrefixedString2' }, + { name: '', field: 'PrefixedString2' }, + { name: 'SessionKey', field: 'PrefixedString2' }, + { name: 'GameId', field: 'PrefixedString2' }, + { name: '', field: 'Dword' }, + ]); + message.deserialize(testBuffer); + }); + + it('should correctly parse the full message', () => { + expect(message.header.messageId).toBe(0x0501); + expect(message.header.messageVersion).toBe(1); + }); + + it('should retrieve field values by name', () => { + const sessionKey = message.getFieldValueByName('SessionKey'); + expect(sessionKey).toBe( + '228AA31B7CB50F23493259270A8B3D0523354F1F296A12440DEA49B501E8C63FD8A9AAE1CF540BB3555BE6472FAB20D620640B99FEE87AA97326980B104023A1A25B3E651E4A44E4E9BB13AE6D00382305DCC6905CA5902645D32F18A6DAD4D346970E1D9119DBE467F7336DC3C3DD9A0CD9BF7A19EE31261D9EDAFA194DED8F', + ); + }); + + it('should serialize back to the original hex string', () => { + const serialized = message.serialize(); + expect(serialized.toString('hex')).toBe(testHex); + }); + + it('should throw error when accessing non-existent field', () => { + expect(() => { + message.getFieldValueByName('NonExistentField'); + }).toThrowError('Field NonExistentField not found'); + }); + }); + + describe('BytableContainer', () => { + describe('Non-null-terminated mode', () => { + let container: BytableContainer; + + beforeEach(() => { + container = new BytableContainer(); + container.setNullTerminated(false); + }); + + it('should dynamically adjust length to match content', () => { + container.setValue('hello'); + expect(container.getLength()).toBe(5); + expect(container.getValue()).toBe('hello'); + + container.setValue('longer string'); + expect(container.getLength()).toBe(13); + expect(container.getValue()).toBe('longer string'); + }); + + it('should serialize with length prefix', () => { + container.setValue('test'); + const serialized = container.serialize(); + // First 4 bytes should be length (4), followed by "test" + expect(serialized.length).toBe(8); // 4 bytes length + 4 bytes content + expect(serialized.readUInt32BE(0)).toBe(4); // Length prefix + expect(serialized.subarray(4).toString()).toBe('test'); + }); + + it('should calculate correct serializeSize', () => { + container.setValue('hello'); + expect(container.serializeSize).toBe(9); // 5 (content) + 4 (length prefix) + }); + }); + + describe('Null-terminated mode', () => { + let container: BytableContainer; + + beforeEach(() => { + container = new BytableContainer(); + container.setNullTerminated(true); + }); + + it('should return a single null terminator when serializing empty string', () => { + container.setNullTerminated(true); + const serialized = container.serialize(); + expect(serialized.toString('hex')).toBe('00'); + }); + + it('should serialize without length prefix', () => { + container.setNullTerminated(true); + container.setValue('test'); + const serialized = container.serialize(); + expect(serialized.toString('hex')).toBe('7465737400'); + }); + + it('should calculate correct serializeSize', () => { + container.setNullTerminated(true); + container.setValue('hello'); + expect(container.serializeSize).toBe(6); // Just the content length (including null terminator) + }); + + it('should throw when setting length for null-terminated container', () => { + container.setNullTerminated(true); + + // Can't change length after setting value + expect(() => container.setLength(5)).toThrowError( + 'Cannot set length for null terminated container', + ); + }); + }); + + describe('Edge cases', () => { + let container: BytableContainer; + + beforeEach(() => { + container = new BytableContainer(); + }); + + it('should handle empty strings', () => { + container.setValue(''); + expect(container.getLength()).toBe(0); + expect(container.getValue()).toBe(''); + }); + + it('should throw error when setting empty buffer in null-terminated mode', () => { + container.setNullTerminated(true); + expect(() => container.setValue(Buffer.alloc(0))).toThrowError( + 'Cannot set empty buffer', + ); + }); + }); + }); + + describe('BytableDword', () => { + it('should throw error when deserializing buffer with insufficient length', () => { const dword = new BytableDword(); - expect(() => dword.deserialize(Buffer.alloc(3))).toThrowError("Cannot deserialize buffer with insufficient length"); + expect(() => dword.deserialize(Buffer.alloc(3))).toThrowError( + 'Cannot deserialize buffer with insufficient length', + ); }); - it("should correctly parse 32-bit values", () => { - const dword = BytableDword.fromBuffer(testBuffer, testBuffer.length - 4); - expect(dword.serializeSize).toBe(4); - // Add specific value checks based on your expected data - }); - }); -}); \ No newline at end of file + it('should correctly parse 32-bit values', () => { + const dword = BytableDword.fromBuffer( + testBuffer, + testBuffer.length - 4, + ); + expect(dword.serializeSize).toBe(4); + // Add specific value checks based on your expected data + }); + }); +}); diff --git a/libs/@rustymotors/binary/src/lib/binary.ts b/libs/@rustymotors/binary/src/lib/binary.ts deleted file mode 100644 index 970a2b1a5..000000000 --- a/libs/@rustymotors/binary/src/lib/binary.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BytableMessage } from "./BytableMessage"; -import { GamePacket } from "rusty-motors-shared-packets"; - -export function binary(): string { - return ""; -} - -export function fromGamePacket(gamePacket: GamePacket): BytableMessage { - const bytable = new BytableMessage(); - bytable.deserialize(gamePacket.serialize()); - return bytable; -} - -export function toGamePacket(bytable: BytableMessage): GamePacket { - const gamePacket = new GamePacket(); - gamePacket.deserialize(bytable.serialize()); - return gamePacket; -} diff --git a/libs/@rustymotors/binary/src/lib/types.ts b/libs/@rustymotors/binary/src/lib/types.ts index f1c403915..278aa1023 100644 --- a/libs/@rustymotors/binary/src/lib/types.ts +++ b/libs/@rustymotors/binary/src/lib/types.ts @@ -1,13 +1,50 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +/** + * A field that can be serialized and deserialized + */ +export interface Field { + /** + * The name of the field + */ + name: string; + /** + * The size of the field when serialized + */ + get serializeSize(): number; + /** + * Serialize the field into a buffer + */ + serialize(): Buffer; + /** + * Deserialize the field from a buffer + * @param buffer The buffer to deserialize from + */ + deserialize(buffer: Buffer): void; +} + export interface BytableObject { serialize(): Buffer; deserialize(buffer: Buffer): void; - json: any; + json: Record; toString(): string; get serializeSize(): number; - getUint16(offset: number, littleEndian: boolean): number; - getUint32(offset: number, littleEndian: boolean): number; get name(): string; get value(): string | number | Buffer; setName(name: string): void; setValue(value: string | number | Buffer): void; -} \ No newline at end of file +} diff --git a/package.json b/package.json index 79a42e44c..ebc311530 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "check:all": "pnpm run -r --stream check", "lint:all": "pnpm run -r --stream lint", "format:all": "pnpm run -r --stream format", - "test:root": "nx test run --coverage test", "test:packages": "pnpm run -r --stream test", "test": "make test", "types:db": "pnpm --filter=rusty-motors-database run types:db", @@ -51,6 +50,7 @@ "rusty-motors-database": "link:packages/database", "rusty-motors-gateway": "link:packages/gateway", "rusty-motors-lobby": "link:packages/lobby", + "rusty-motors-logger": "workspace:^", "rusty-motors-login": "link:packages/login", "rusty-motors-nps": "link:packages/nps", "rusty-motors-personas": "link:packages/persona", @@ -67,16 +67,9 @@ "@databases/pg-migrations": "^5.0.3", "@databases/pg-schema-cli": "^4.4.0", "@databases/pg-test": "^3.1.2", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.27.0", - "@nx/eslint": "21.0.3", - "@nx/js": "21.0.3", - "@nx/node": "21.0.3", - "@nx/vite": "21.0.3", - "@nx/web": "21.0.3", "@sentry/cli": "^2.45.0", "@swc-node/register": "~1.10.10", - "@swc/core": "~1.11.22", + "@swc/core": "~1.11.24", "@swc/helpers": "~0.5.17", "@tsconfig/node-lts": "^22.0.1", "@tsconfig/node20": "^20.1.5", @@ -85,25 +78,16 @@ "@types/node": "^22.15.18", "@types/sinon": "17.0.4", "@types/sinon-chai": "4.0.0", - "@typescript-eslint/eslint-plugin": "^8.32.1", - "@typescript-eslint/parser": "^8.32.1", "@vitest/coverage-v8": "^3.1.3", "@vitest/ui": "^3.1.3", - "eslint": "^9.27.0", - "eslint-config-prettier": "^10.1.5", - "eslint-plugin-prettier": "5.4.0", "globals": "^16.1.0", "husky": "^9.1.7", "lint-staged": "^16.0.0", - "nx": "21.0.3", - "prettier": "3.5.3", - "prettier-eslint": "16.4.2", "rimraf": "^6.0.1", "standard-version": "^9.5.0", "tslib": "^2.8.1", "tsx": "4.19.4", "typescript": "^5.8.3", - "typescript-eslint-language-service": "^5.0.5", "vite": "^6.3.5", "vitest": "^3.1.3" }, @@ -132,8 +116,13 @@ }, "packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977", "pnpm": { - "overrides": { - "tinymce": "^7.9.0" - } + "onlyBuiltDependencies": [ + "@biomejs/biome", + "@sentry-internal/node-cpu-profiler", + "@sentry/cli", + "@swc/core", + "bcrypt", + "esbuild" + ] } } diff --git a/packages/database/index.ts b/packages/database/index.ts index da76d2fc6..cdd9d0013 100644 --- a/packages/database/index.ts +++ b/packages/database/index.ts @@ -1,8 +1,5 @@ -export { - type DatabaseManager, - databaseManager, - getDatabase, -} from "./src/DatabaseManager.js"; -export { databaseService, findCustomerByContext } from "./src/databaseService.js"; -export { getTunables as getTuneables } from "./src/services/tunables.js"; +export { + databaseService, +} from './src/databaseService.js'; +export { getTunables as getTuneables } from './src/services/tunables.js'; diff --git a/packages/database/src/DatabaseManager.ts b/packages/database/src/DatabaseManager.ts deleted file mode 100644 index 0aa389b81..000000000 --- a/packages/database/src/DatabaseManager.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type { ConnectionRecord } from "rusty-motors-shared"; -import { Sequelize } from "sequelize"; - - - -// This is a fake database table that holds sessions of currently logged in users -const _sessions: ConnectionRecord[] = []; -// This is a fake database table that holds user data -const _users: Map = new Map(); - -/** - * @module Database - */ - -/** - * Update a user record in the database - -* @throws {Error} If the user record is not found - */ -async function updateUser(user: { - userId: number; - userData: Buffer; -}): Promise { - try { - _users.set(user.userId, user.userData); - return Promise.resolve(); - } catch (error) { - throw Error(`Error updating user: ${String(error)}`); - } -} - -/** - * Locate customer session encryption key in the database - * - * @throws {Error} If the session key is not found - */ -async function fetchSessionKeyByCustomerId( - customerId: number, -): Promise { - const record = _sessions.find((session) => { - return session.customerId === customerId; - }); - if (typeof record === "undefined") { - throw Error(`Session key not found for customer ${customerId}`); - } - return Promise.resolve(record); -} - -/** - * Create or overwrite a customer's session key record - * - * @param {number} customerId - * @param {string} sessionKey - * @param {string} contextId - * @param {string} connectionId - * @returns {Promise} - */ -async function updateSessionKey( - customerId: number, - sessionKey: string, - contextId: string, - connectionId: string, -): Promise { - const sKey = sessionKey.slice(0, 16); - - const updatedSession: ConnectionRecord = { - customerId, - sessionKey, - sKey, - contextId, - connectionId, - }; - - const record = _sessions.findIndex((session) => { - return session.customerId === customerId; - }); - - _sessions.splice(record, 1, updatedSession); - - return Promise.resolve(); -} - -/** - * Locate customer session encryption key in the database - * - * @param {string} connectionId - * @returns {Promise} - * @throws {Error} If the session key is not found - */ -async function fetchSessionKeyByConnectionId( - connectionId: string, -): Promise { - const record = _sessions.find((session) => { - return session.connectionId === connectionId; - }); - if (typeof record === "undefined") { - throw Error(`Session key not found for connection ${connectionId}`); - } - return Promise.resolve(record); -} - -let database: Sequelize; - - -export function getDatabase(): Sequelize { - if (!database) { - const databaseUrl = process.env["DATABASE_URL"]; - if ( typeof databaseUrl === "undefined" ) { - throw new Error("DATABASE_URL is not defined"); - } - - database = new Sequelize(databaseUrl, { - logging: false, - }); - } - return database; -} - -export interface DatabaseManager { - updateUser: typeof updateUser; - fetchSessionKeyByCustomerId: typeof fetchSessionKeyByCustomerId; - updateSessionKey: typeof updateSessionKey; - fetchSessionKeyByConnectionId: typeof fetchSessionKeyByConnectionId; -} - -export const databaseManager: DatabaseManager = { - updateUser, - fetchSessionKeyByCustomerId, - updateSessionKey, - fetchSessionKeyByConnectionId, -}; diff --git a/packages/database/src/databaseService.ts b/packages/database/src/databaseService.ts index fbb506902..cc233c15a 100644 --- a/packages/database/src/databaseService.ts +++ b/packages/database/src/databaseService.ts @@ -1,168 +1,277 @@ -import { hashSync } from "bcrypt"; -import { DatabaseSync } from "node:sqlite"; -import { getServerLogger } from "rusty-motors-shared"; -import type { UserRecordMini } from "rusty-motors-shared"; -import { SQL, DATABASE_PATH } from "./databaseConstrants"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { hashSync } from 'bcrypt'; +import { DatabaseSync } from 'node:sqlite'; +import { getServerLogger } from 'rusty-motors-logger'; +import type { UserRecordMini, ConnectionRecord } from 'rusty-motors-shared'; +import { SQL, DATABASE_PATH } from './databaseConstrants.js'; +import { Sequelize, DataTypes, Model } from 'sequelize'; + +// Sequelize models for UserAccount and AuthTicket +const sequelize = new Sequelize(process.env['DATABASE_URL'] || 'sqlite::memory:', { logging: false }); + +class UserAccount extends Model { + declare username: string; + declare password: string; + declare ticket: string; + declare customerId: string; +} +UserAccount.init({ + username: { type: DataTypes.STRING, allowNull: false, unique: true }, + password: { type: DataTypes.STRING, allowNull: false }, + ticket: { type: DataTypes.STRING, allowNull: false }, + customerId: { type: DataTypes.STRING, allowNull: false, unique: true }, +}, { sequelize, modelName: 'UserAccount', tableName: 'user_accounts', timestamps: false }); + +class AuthTicket extends Model { + declare ticket: string; + declare customerId: string; +} +AuthTicket.init({ + ticket: { type: DataTypes.STRING, allowNull: false, unique: true }, + customerId: { type: DataTypes.STRING, allowNull: false }, +}, { sequelize, modelName: 'AuthTicket', tableName: 'auth_tickets', timestamps: false }); + +// In-memory session and user stores (for legacy/fallback/testing) +const _sessions: ConnectionRecord[] = []; +const _users: Map = new Map(); // Database Service Interface export interface DatabaseService { - isDatabaseConnected: () => boolean; - registerUser: ( - username: string, - password: string, - customerId: number, - ) => void; - findUser: (username: string, password: string) => UserRecordMini; - getAllUsers: () => UserRecordMini[]; - updateSession: ( - customerId: number, - contextId: string, - userId: number, - ) => void; - findSessionByContext: (contextId: string) => UserRecordMini | undefined; - } + isDatabaseConnected: () => boolean; + registerUser: ( + username: string, + password: string, + customerId: number, + ) => void; + findUser: (username: string, password: string) => UserRecordMini; + getAllUsers: () => UserRecordMini[]; + updateSession: ( + customerId: number, + contextId: string, + userId: number, + ) => void; + findSessionByContext: (contextId: string) => UserRecordMini | undefined; + updateUser: (user: { userId: number; userData: Buffer }) => Promise; + fetchSessionKeyByCustomerId: (customerId: number) => Promise; + updateSessionKey: (customerId: number, sessionKey: string, contextId: string, connectionId: string) => Promise; + fetchSessionKeyByConnectionId: (connectionId: string) => Promise; + retrieveUserAccount: ( + username: string, + password: string, + ) => { username: string; ticket: string; customerId: string } | null; + generateTicket: (customerId: string) => string; + generateTicketAsync: (customerId: string) => Promise; + retrieveUserAccountAsync: ( + username: string, + password: string, + ) => Promise<{ username: string; ticket: string; customerId: string } | null>; +} // Database Implementation -export const DatabaseImpl = { - /** - * Generates a hashed password using bcrypt - * @param password - The plain text password to hash - * @param saltRounds - Number of salt rounds for bcrypt (default: 10) - * @returns The hashed password string - */ - generatePasswordHash(password: string, saltRounds = 10): string { - const hash = hashSync(password, saltRounds); - return hash; - }, - - /** - * Initializes the database schema by creating necessary tables and indexes - * @param database - The SQLite database instance - */ - initializeDatabase(database: DatabaseSync) { - database.exec(SQL.CREATE_USER_TABLE); - database.exec( - "CREATE INDEX IF NOT EXISTS idx_user_username ON user(username)", - ); - database.exec( - "CREATE INDEX IF NOT EXISTS idx_user_customerId ON user(customerId)", - ); - database.exec(SQL.CREATE_SESSION_TABLE); - database.exec( - "CREATE INDEX IF NOT EXISTS idx_session_customerId ON session(customerId)", - ); - }, - - /** - * Registers a new user in the database - * @param database - The SQLite database instance - * @param username - Unique username for the new user - * @param password - User's password (will be hashed) - * @param customerId - Associated customer ID - * @throws Error if registration fails for reasons other than duplicate username - */ - registerNewUser( - database: DatabaseSync, - username: string, - password: string, - customerId: number, - ) { - const logger = getServerLogger("database"); - const hashedPassword = this.generatePasswordHash(password); - try { - database - .prepare(SQL.INSERT_USER) - .run(username, hashedPassword, customerId); - } catch (error) { - if ( - error instanceof Error && - error.message.includes("UNIQUE constraint failed") - ) { - logger.warn(`User ${username} already exists`); - return; - } - throw error; - } - }, - - /** - * Finds a user by username and password - * @param database - The SQLite database instance - * @param username - Username to search for - * @param password - Password to verify - * @returns UserRecordMini object containing user details - * @throws Error if user is not found - */ - findUser( - database: DatabaseSync, - username: string, - password: string, - ): UserRecordMini { - const query = database.prepare(SQL.FIND_USER); - const hashedPassword = this.generatePasswordHash(password); - const user = query.get(username, hashedPassword) as UserRecordMini | null; - if (user == null) { - throw new Error("User not found"); - } - return { - customerId: user.customerId, - profileId: user.profileId, - contextId: user.contextId, - }; - }, - - /** - * Retrieves all users from the database - * @param database - The SQLite database instance - * @returns Array of UserRecordMini objects - */ - getAllUsers(database: DatabaseSync): UserRecordMini[] { - const query = database.prepare(SQL.GET_ALL_USERS); - const users = query.all() as UserRecordMini[]; - return users; - }, - - /** - * Updates or creates a new session for a user - * @param database - The SQLite database instance - * @param customerId - Customer ID associated with the session - * @param contextId - Unique context ID for the session - * @param userId - ID of the user owning the session - */ - updateSession( - database: DatabaseSync, - customerId: number, - contextId: string, - profileId: number, - ) { - const insert = database.prepare(SQL.UPDATE_SESSION); - insert.run(contextId, customerId, profileId); - }, - - findSessionByContext( - database: DatabaseSync, - contextId: string, - ): UserRecordMini | undefined { - const query = database.prepare(SQL.FIND_SESSION_BY_CONTEXT); - const user = query.get(contextId) as UserRecordMini | undefined; - return user; - }, - - /** - * Creates a DatabaseService interface implementation - * @param db - The SQLite database instance - * @returns DatabaseService interface with implemented database operations - */ - createDatabaseService(db: DatabaseSync): DatabaseService { - return { - isDatabaseConnected: () => db !== null, - registerUser: (...args) => this.registerNewUser(db, ...args), - findUser: (...args) => this.findUser(db, ...args), - getAllUsers: () => this.getAllUsers(db), - updateSession: (...args) => this.updateSession(db, ...args), - findSessionByContext: (...args) => this.findSessionByContext(db, ...args), - }; - }, +const DatabaseImpl = { + /** + * Generates a hashed password using bcrypt + * @param password - The plain text password to hash + * @param saltRounds - Number of salt rounds for bcrypt (default: 10) + * @returns The hashed password string + */ + generatePasswordHash(password: string, saltRounds = 10): string { + const hash = hashSync(password, saltRounds); + return hash; + }, + + /** + * Initializes the database schema by creating necessary tables and indexes + * @param database - The SQLite database instance + */ + initializeDatabase(database: DatabaseSync) { + database.exec(SQL.CREATE_USER_TABLE); + database.exec( + 'CREATE INDEX IF NOT EXISTS idx_user_username ON user(username)', + ); + database.exec( + 'CREATE INDEX IF NOT EXISTS idx_user_customerId ON user(customerId)', + ); + database.exec(SQL.CREATE_SESSION_TABLE); + database.exec( + 'CREATE INDEX IF NOT EXISTS idx_session_customerId ON session(customerId)', + ); + }, + + /** + * Registers a new user in the database + * @param database - The SQLite database instance + * @param username - Unique username for the new user + * @param password - User's password (will be hashed) + * @param customerId - Associated customer ID + * @throws Error if registration fails for reasons other than duplicate username + */ + registerNewUser( + database: DatabaseSync, + username: string, + password: string, + customerId: number, + ) { + const logger = getServerLogger('database'); + const hashedPassword = this.generatePasswordHash(password); + try { + database + .prepare(SQL.INSERT_USER) + .run(username, hashedPassword, customerId); + } catch (error) { + if ( + error instanceof Error && + error.message.includes('UNIQUE constraint failed') + ) { + logger.warn(`User ${username} already exists`); + return; + } + throw error; + } + }, + + /** + * Finds a user by username and password + * @param database - The SQLite database instance + * @param username - Username to search for + * @param password - Password to verify + * @returns UserRecordMini object containing user details + * @throws Error if user is not found + */ + findUser( + database: DatabaseSync, + username: string, + password: string, + ): UserRecordMini { + const query = database.prepare(SQL.FIND_USER); + const hashedPassword = this.generatePasswordHash(password); + const user = query.get( + username, + hashedPassword, + ) as unknown as UserRecordMini | null; + if (user == null) { + throw new Error('User not found'); + } + return { + customerId: user.customerId, + profileId: user.profileId, + contextId: user.contextId, + }; + }, + + /** + * Retrieves all users from the database + * @param database - The SQLite database instance + * @returns Array of UserRecordMini objects + */ + getAllUsers(database: DatabaseSync): UserRecordMini[] { + const query = database.prepare(SQL.GET_ALL_USERS); + const users = query.all() as unknown as UserRecordMini[]; + return users; + }, + + /** + * Updates or creates a new session for a user + * @param database - The SQLite database instance + * @param customerId - Customer ID associated with the session + * @param contextId - Unique context ID for the session + * @param userId - ID of the user owning the session + */ + updateSession( + database: DatabaseSync, + customerId: number, + contextId: string, + profileId: number, + ) { + const insert = database.prepare(SQL.UPDATE_SESSION); + insert.run(contextId, customerId, profileId); + }, + + findSessionByContext( + database: DatabaseSync, + contextId: string, + ): UserRecordMini | undefined { + const query = database.prepare(SQL.FIND_SESSION_BY_CONTEXT); + const user = query.get(contextId) as UserRecordMini | undefined; + return user; + }, + + /** + * Creates a DatabaseService interface implementation + * @param db - The SQLite database instance + * @returns DatabaseService interface with implemented database operations + */ + createDatabaseService(db: DatabaseSync): DatabaseService { + return { + isDatabaseConnected: () => db !== null, + registerUser: (...args) => this.registerNewUser(db, ...args), + findUser: (...args) => this.findUser(db, ...args), + getAllUsers: () => this.getAllUsers(db), + updateSession: (...args) => this.updateSession(db, ...args), + findSessionByContext: (contextId: string) => { + return DatabaseImpl.findSessionByContext(db, contextId); + }, + updateUser: async (user) => { + try { + _users.set(user.userId, user.userData); + return Promise.resolve(); + } catch (error) { + throw Error(`Error updating user: ${String(error)}`); + } + }, + fetchSessionKeyByCustomerId: async (customerId: number) => { + const record = _sessions.find((session) => session.customerId === customerId); + if (typeof record === 'undefined') { + throw Error(`Session key not found for customer ${customerId}`); + } + return Promise.resolve(record); + }, + updateSessionKey: async (customerId: number, sessionKey: string, contextId: string, connectionId: string) => { + const sKey = sessionKey.slice(0, 16); + const updatedSession: ConnectionRecord = { + customerId, + sessionKey, + sKey, + contextId, + connectionId, + }; + const record = _sessions.findIndex((session) => session.customerId === customerId); + _sessions.splice(record, 1, updatedSession); + return Promise.resolve(); + }, + fetchSessionKeyByConnectionId: async (connectionId: string) => { + const record = _sessions.find((session) => session.connectionId === connectionId); + if (typeof record === 'undefined') { + throw Error(`Session key not found for connection ${connectionId}`); + } + return Promise.resolve(record); + }, + // Synchronous stubs for legacy API, always throw + generateTicket: (_customerId: string) => { + throw new Error('generateTicket should be called as an async method'); + }, + retrieveUserAccount: (_username: string, _password: string) => { + throw new Error('retrieveUserAccount should be called as an async method'); + }, + generateTicketAsync: generateTicketAsync, + retrieveUserAccountAsync: retrieveUserAccountAsync, + }; + }, } as const; // Database Instance Management @@ -173,36 +282,49 @@ let databaseInstance: DatabaseSync | null = null; * @returns DatabaseService interface with database operations */ function initializeDatabaseService(): DatabaseService { - if (databaseInstance === null) { - databaseInstance = new DatabaseSync(DATABASE_PATH); - DatabaseImpl.initializeDatabase(databaseInstance); - DatabaseImpl.registerNewUser(databaseInstance, "admin", "admin", 5551212); - DatabaseImpl.updateSession( - databaseInstance, - 1212555, - "5213dee3a6bcdb133373b2d4f3b9962758", - 1, - ); - DatabaseImpl.updateSession( - databaseInstance, - 5551212, - "d316cd2dd6bf870893dfbaaf17f965884e", - 2, - ); - getServerLogger("database").info("Database initialized"); - } - - return DatabaseImpl.createDatabaseService(databaseInstance); + if (databaseInstance === null) { + databaseInstance = new DatabaseSync(DATABASE_PATH); + DatabaseImpl.initializeDatabase(databaseInstance); + DatabaseImpl.registerNewUser( + databaseInstance, + 'admin', + 'admin', + 5551212, + ); + DatabaseImpl.updateSession( + databaseInstance, + 1212555, + '5213dee3a6bcdb133373b2d4f3b9962758', + 1, + ); + DatabaseImpl.updateSession( + databaseInstance, + 5551212, + 'd316cd2dd6bf870893dfbaaf17f965884e', + 2, + ); + getServerLogger('database').info('Database initialized'); + } + + return DatabaseImpl.createDatabaseService(databaseInstance); } +// Add async version for real use +export async function generateTicketAsync(customerId: string): Promise { + const ticket = await AuthTicket.findOne({ where: { customerId } }); + return ticket ? ticket.ticket : ''; +} -export function findCustomerByContext( - contextId: string, -): UserRecordMini | undefined { - const database = initializeDatabaseService(); - const user = database.findSessionByContext(contextId); - return user; +// Add async version for real use +export async function retrieveUserAccountAsync(username: string, password: string): Promise<{ username: string; ticket: string; customerId: string } | null> { + const user = await UserAccount.findOne({ where: { username, password } }); + if (!user) return null; + return { username: user.username, ticket: user.ticket, customerId: user.customerId }; } + +// --- Begin migrated DatabaseManager API --- +// --- End migrated DatabaseManager API --- + // Exported Database Service Instance export const databaseService: DatabaseService = initializeDatabaseService(); diff --git a/packages/gateway/src/GatewayServer.ts b/packages/gateway/src/GatewayServer.ts index 8f1256b86..c680d66a2 100644 --- a/packages/gateway/src/GatewayServer.ts +++ b/packages/gateway/src/GatewayServer.ts @@ -1,210 +1,211 @@ -import { Socket, createServer as createSocketServer } from "node:net"; -import { Configuration, getServerConfiguration, getServerLogger, ServerLogger } from "rusty-motors-shared"; -import { createInitialState } from "rusty-motors-shared"; -import { onSocketConnection } from "./index.js"; -import { initializeRouteHandlers, processHttpRequest } from "./web.js"; -import type { GatewayOptions } from "./types.js"; -import { addPortRouter } from "./portRouters.js"; -import { npsPortRouter } from "./npsPortRouter.js"; -import { mcotsPortRouter } from "./mcotsPortRouter.js"; -import http from "node:http"; -import { HotkeyManager } from "./HotkeyManager.js"; - +import { Socket, createServer as createSocketServer } from 'node:net'; +import { Configuration, getServerConfiguration } from 'rusty-motors-shared'; +import { createInitialState } from 'rusty-motors-shared'; +import { onSocketConnection } from './index.js'; +import { initializeRouteHandlers, processHttpRequest } from './web.js'; +import type { GatewayOptions } from './types.js'; +import { addPortRouter } from './portRouters.js'; +import { npsPortRouter } from './npsPortRouter.js'; +import { mcotsPortRouter } from './mcotsPortRouter.js'; +import http from 'node:http'; +import { HotkeyManager } from './HotkeyManager.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** * Gateway server * @see {@link getGatewayServer()} to get a singleton instance */ export class Gateway { - config: Configuration; - log: ServerLogger; - timer: NodeJS.Timeout | null; - loopInterval: number; - status: string; - consoleEvents: string[]; - backlogAllowedCount: number; - listeningPortList: number[]; - activeServers: import("node:net").Server[]; - socketconnection: ({ - incomingSocket, - log, - }: { - incomingSocket: Socket; - log?: ServerLogger; - }) => void; - webServer: http.Server; - /** - * Creates an instance of GatewayServer. - * @param {GatewayOptions} options - */ - constructor({ - config = getServerConfiguration(), - log = getServerLogger("Gateway"), - backlogAllowedCount = 0, - listeningPortList = [], - socketConnectionHandler = onSocketConnection, - }: GatewayOptions) { - log.debug("Creating GatewayServer instance"); - - this.config = config; - this.log = log; - /** @type {NodeJS.Timeout | null} */ - this.timer = null; - this.loopInterval = 0; - /** @type {"stopped" | "running" | "stopping" | "restarting"} */ - this.status = "stopped"; - this.consoleEvents = ["userExit", "userRestart", "userHelp"]; - this.backlogAllowedCount = backlogAllowedCount; - this.listeningPortList = listeningPortList; - /** @type {import("node:net").Server[]} */ - this.activeServers = []; - this.socketconnection = socketConnectionHandler; - - initializeRouteHandlers(); - - this.webServer = http.createServer(processHttpRequest); - } - - /** - * Starts the GatewayServer. - * - * This method initializes the server, starts new servers on the specified ports, - * and sets up the web server connection. If the web server is not defined, it throws an error. - * Finally, it updates the server status to "running". - * - * @throws {Error} If the web server is undefined. - */ - start(): void { - // Initialize the GatewayServer - this.init(); - - this.listeningPortList.forEach(async (port) => { - this.startNewServer(port, this.socketconnection); - }); - - if (this.webServer === undefined) { - throw Error("webServer is undefined"); - } - this.startNewServer(3000, ({ incomingSocket }) => { - this.webServer.emit("connection", incomingSocket); - }); - - this.status = "running"; - - new HotkeyManager(); - - } - - /** - * Starts a new server on the specified port and sets up a socket connection handler. - * - * @param port - The port number on which the server will listen. - * @param socketConnectionHandler - A callback function that handles incoming socket connections. - * @param socketConnectionHandler.incomingSocket - The incoming socket connection. - */ - private startNewServer( - port: number, - socketConnectionHandler: ({ - incomingSocket, - }: { incomingSocket: Socket }) => void, - ) { - const server = createSocketServer((s) => { - socketConnectionHandler({ incomingSocket: s }); - }); - - // Listen on the specified port - server.listen(port, "0.0.0.0", this.backlogAllowedCount, () => { - this.log.debug(`Listening on port ${port}`); - }); - - // Add the server to the list of servers - this.activeServers.push(server); - } - - /** - * Gracefully stops the GatewayServer and exits the process. - * - * This method first stops the GatewayServer by calling the `stop` method, - * and then exits the Node.js process with a status code of 0. - * - * @returns {Promise} A promise that resolves when the server has stopped and the process has exited. - */ - async exit(): Promise { - // Stop the GatewayServer - await this.stop(); - - // Exit the process - process.exit(0); - } - - /** - * Stops the GatewayServer. - * - * This method performs the following actions: - * 1. Marks the GatewayServer as stopping. - * 2. Stops the servers by calling `shutdownServers`. - * 3. Stops the timer if it is running. - * 4. Marks the GatewayServer as stopped. - * 5. Resets the global state by creating and saving the initial state. - * - * @returns {Promise} A promise that resolves when the server has been stopped. - */ - async stop(): Promise { - // Mark the GatewayServer as stopping - this.log.debug("Marking GatewayServer as stopping"); - this.status = "stopping"; - - // Stop the servers - await this.shutdownServers(); - - // Stop the timer - if (this.timer !== null) { - clearInterval(this.timer); - } - - // Mark the GatewayServer as stopped - this.log.debug("Marking GatewayServer as stopped"); - this.status = "stopped"; - - // Reset the global state - this.log.debug("Resetting the global state"); - createInitialState({}).save(); - } - - /** - * Shuts down all active servers and emits a close event on the web server. - * - * @throws {Error} If the webServer is undefined. - * @private - * @async - */ - private async shutdownServers() { - this.log.info("Shutting down servers"); - this.activeServers.forEach((server) => { - server.close(); - }); - - if (this.webServer === undefined) { - throw Error("webServer is undefined"); - } - this.webServer.emit("close"); - } - - /** - * Initializes the GatewayServer by setting up the web server and registering routes. - * - * - Creates a Fastify web server instance. - * - Registers the FastifySensible plugin for additional utilities. - * - Adds port routers for various ports to handle incoming requests. - * - Sets up a signal handler to gracefully exit on SIGINT. - */ - private init() { - addPortRouter(8226, npsPortRouter); - addPortRouter(8227, npsPortRouter); - addPortRouter(8228, npsPortRouter); - addPortRouter(7003, npsPortRouter); - addPortRouter(43300, mcotsPortRouter); - - process.on("SIGINT", this.exit.bind(this)); - } + config: Configuration; + log: ServerLogger; + timer: NodeJS.Timeout | null; + loopInterval: number; + status: string; + consoleEvents: string[]; + backlogAllowedCount: number; + listeningPortList: number[]; + activeServers: import('node:net').Server[]; + socketconnection: ({ + incomingSocket, + log, + }: { + incomingSocket: Socket; + log?: ServerLogger; + }) => void; + webServer: http.Server; + /** + * Creates an instance of GatewayServer. + * @param {GatewayOptions} options + */ + constructor({ + config = getServerConfiguration(), + log = getServerLogger('Gateway'), + backlogAllowedCount = 0, + listeningPortList = [], + socketConnectionHandler = onSocketConnection, + }: GatewayOptions) { + log.debug('Creating GatewayServer instance'); + + this.config = config; + this.log = log; + /** @type {NodeJS.Timeout | null} */ + this.timer = null; + this.loopInterval = 0; + /** @type {"stopped" | "running" | "stopping" | "restarting"} */ + this.status = 'stopped'; + this.consoleEvents = ['userExit', 'userRestart', 'userHelp']; + this.backlogAllowedCount = backlogAllowedCount; + this.listeningPortList = listeningPortList; + /** @type {import("node:net").Server[]} */ + this.activeServers = []; + this.socketconnection = socketConnectionHandler; + + initializeRouteHandlers(); + + this.webServer = http.createServer(processHttpRequest); + } + + /** + * Starts the GatewayServer. + * + * This method initializes the server, starts new servers on the specified ports, + * and sets up the web server connection. If the web server is not defined, it throws an error. + * Finally, it updates the server status to "running". + * + * @throws {Error} If the web server is undefined. + */ + start(): void { + // Initialize the GatewayServer + this.init(); + + this.listeningPortList.forEach(async (port) => { + this.startNewServer(port, this.socketconnection); + }); + + if (this.webServer === undefined) { + throw Error('webServer is undefined'); + } + this.startNewServer(3000, ({ incomingSocket }) => { + this.webServer.emit('connection', incomingSocket); + }); + + this.status = 'running'; + + new HotkeyManager(); + } + + /** + * Starts a new server on the specified port and sets up a socket connection handler. + * + * @param port - The port number on which the server will listen. + * @param socketConnectionHandler - A callback function that handles incoming socket connections. + * @param socketConnectionHandler.incomingSocket - The incoming socket connection. + */ + private startNewServer( + port: number, + socketConnectionHandler: ({ + incomingSocket, + }: { + incomingSocket: Socket; + }) => void, + ) { + const server = createSocketServer((s) => { + socketConnectionHandler({ incomingSocket: s }); + }); + + // Listen on the specified port + server.listen(port, '0.0.0.0', this.backlogAllowedCount, () => { + this.log.debug(`Listening on port ${port}`); + }); + + // Add the server to the list of servers + this.activeServers.push(server); + } + + /** + * Gracefully stops the GatewayServer and exits the process. + * + * This method first stops the GatewayServer by calling the `stop` method, + * and then exits the Node.js process with a status code of 0. + * + * @returns {Promise} A promise that resolves when the server has stopped and the process has exited. + */ + async exit(): Promise { + // Stop the GatewayServer + await this.stop(); + + // Exit the process + process.exit(0); + } + + /** + * Stops the GatewayServer. + * + * This method performs the following actions: + * 1. Marks the GatewayServer as stopping. + * 2. Stops the servers by calling `shutdownServers`. + * 3. Stops the timer if it is running. + * 4. Marks the GatewayServer as stopped. + * 5. Resets the global state by creating and saving the initial state. + * + * @returns {Promise} A promise that resolves when the server has been stopped. + */ + async stop(): Promise { + // Mark the GatewayServer as stopping + this.log.debug('Marking GatewayServer as stopping'); + this.status = 'stopping'; + + // Stop the servers + await this.shutdownServers(); + + // Stop the timer + if (this.timer !== null) { + clearInterval(this.timer); + } + + // Mark the GatewayServer as stopped + this.log.debug('Marking GatewayServer as stopped'); + this.status = 'stopped'; + + // Reset the global state + this.log.debug('Resetting the global state'); + createInitialState({}).save(); + } + + /** + * Shuts down all active servers and emits a close event on the web server. + * + * @throws {Error} If the webServer is undefined. + * @private + * @async + */ + private async shutdownServers() { + this.log.info('Shutting down servers'); + this.activeServers.forEach((server) => { + server.close(); + }); + + if (this.webServer === undefined) { + throw Error('webServer is undefined'); + } + this.webServer.emit('close'); + } + + /** + * Initializes the GatewayServer by setting up the web server and registering routes. + * + * - Creates a Fastify web server instance. + * - Registers the FastifySensible plugin for additional utilities. + * - Adds port routers for various ports to handle incoming requests. + * - Sets up a signal handler to gracefully exit on SIGINT. + */ + private init() { + addPortRouter(8226, npsPortRouter); + addPortRouter(8227, npsPortRouter); + addPortRouter(8228, npsPortRouter); + addPortRouter(7003, npsPortRouter); + addPortRouter(43300, mcotsPortRouter); + + process.on('SIGINT', this.exit.bind(this)); + } } diff --git a/packages/gateway/src/index.ts b/packages/gateway/src/index.ts index 4c2bc3b6a..d3d7e34df 100644 --- a/packages/gateway/src/index.ts +++ b/packages/gateway/src/index.ts @@ -14,12 +14,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { Socket } from "node:net"; -import { randomUUID } from "node:crypto"; -import { tagSocket } from "./socketUtility.js"; -import { getPortRouter } from "./portRouters.js"; -import * as Sentry from "@sentry/node"; -import { getServerLogger, ServerLogger } from "rusty-motors-shared"; +import { Socket } from 'node:net'; +import { randomUUID } from 'node:crypto'; +import { tagSocket } from './socketUtility.js'; +import { getPortRouter } from './portRouters.js'; +import * as Sentry from '@sentry/node'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** * Handle incoming TCP connections @@ -30,36 +30,32 @@ import { getServerLogger, ServerLogger } from "rusty-motors-shared"; * */ export function onSocketConnection({ - incomingSocket, - log = getServerLogger( "onSocketConnection" ), + incomingSocket, + log = getServerLogger('onSocketConnection'), }: { - incomingSocket: Socket; - log?: ServerLogger; + incomingSocket: Socket; + log?: ServerLogger; }) { - // Get the local port and remote address - const { localPort, remoteAddress } = incomingSocket; + // Get the local port and remote address + const { localPort, remoteAddress } = incomingSocket; - // If the local port or remote address is undefined, throw an error - if (localPort === undefined || remoteAddress === undefined) { - const s = JSON.stringify(incomingSocket); - throw Error("localPort or remoteAddress is undefined: " + s); - } + // If the local port or remote address is undefined, throw an error + if (localPort === undefined || remoteAddress === undefined) { + const s = JSON.stringify(incomingSocket); + throw Error('localPort or remoteAddress is undefined: ' + s); + } - const socketWithId = tagSocket( - incomingSocket, - Date.now(), - randomUUID(), - ); + const socketWithId = tagSocket(incomingSocket, Date.now(), randomUUID()); - /* - * At this point, we have a tagged socket with an ID. - */ + /* + * At this point, we have a tagged socket with an ID. + */ - const portRouter = getPortRouter(localPort); + const portRouter = getPortRouter(localPort); - // Hand the socket to the port router - portRouter({ taggedSocket: socketWithId }).catch((error) => { - Sentry.captureException(error); - log.error(`Error in port router: ${error.message}`); - }); + // Hand the socket to the port router + portRouter({ taggedSocket: socketWithId }).catch((error) => { + Sentry.captureException(error); + log.error(`Error in port router: ${error.message}`); + }); } diff --git a/packages/gateway/src/mcotsPortRouter.ts b/packages/gateway/src/mcotsPortRouter.ts index ac320ea34..d80398053 100644 --- a/packages/gateway/src/mcotsPortRouter.ts +++ b/packages/gateway/src/mcotsPortRouter.ts @@ -1,108 +1,144 @@ -import type { TaggedSocket } from "./socketUtility.js"; +import type { TaggedSocket } from './socketUtility.js'; import { - ServerPacket, - type SerializableInterface, -} from "rusty-motors-shared-packets"; -import { receiveTransactionsData } from "rusty-motors-transactions"; -import * as Sentry from "@sentry/node"; -import { getServerLogger, ServerLogger } from "rusty-motors-shared"; + ServerPacket, + type SerializableInterface, +} from 'rusty-motors-shared-packets'; +import { receiveTransactionsData } from 'rusty-motors-transactions'; +import * as Sentry from '@sentry/node'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** - * Handles the routing of messages for the MCOTS (Motor City Online Transaction Server) ports. - * - * @param taggedSocket - The socket object that contains the tagged information for routing. + * Pure function to parse the initial message buffer into a ServerPacket. */ - -export async function mcotsPortRouter({ - taggedSocket, - log = getServerLogger("gateway.mcotsPortRouter"), -}: { - taggedSocket: TaggedSocket; - log?: ServerLogger; -}): Promise { - const { rawSocket: socket, connectionId: id } = taggedSocket; - - const port = socket.localPort || 0; - - if (port === 0) { - log.error(`[${id}] Local port is undefined`); - socket.end(); - return; - } - - log.debug(`[${id}] MCOTS port router started for port ${port}`); - - // Handle the socket connection here - socket.on("data", async (data) => { - try { - log.debug(`[${id}] Received data: ${data.toString("hex")}`); - const initialPacket = parseInitialMessage(data); - log.debug(`[${id}] Initial packet(str): ${initialPacket}`); - log.debug(`[${id}] initial Packet(hex): ${initialPacket.toHexString()}`); - await routeInitialMessage(id, port, initialPacket) - .then((response) => { - // Send the response back to the client - log.debug(`[${id}] Sending response: ${response.toString("hex")}`); - socket.write(response); - }) - .catch((error) => { - throw new Error(`[${id}] Error routing initial mcots message: ${error}`, { - cause: error, - }); - }); - } catch (error) { - Sentry.captureException(error); - log.error(`[${id}] Error handling data: ${error}`); - } - }); - - socket.on("end", () => { - // log.debug(`[${id}] Socket closed by client for port ${port}`); - }); - - socket.on("error", (error) => { - log.error(`[${id}] Socket error: ${error}`); - }); +export function parseInitialMessage(data: Buffer): ServerPacket { + const initialPacket = new ServerPacket(); + initialPacket.deserialize(data); + return initialPacket; } -function parseInitialMessage(data: Buffer): ServerPacket { - const initialPacket = new ServerPacket(); - initialPacket.deserialize(data); - return initialPacket; -} - -async function routeInitialMessage( - id: string, - port: number, - initialPacket: ServerPacket, - log = getServerLogger("gateway.mcotsPortRouter/routeInitialMessage"), +/** + * Pure function to route the initial message and return a Buffer response. + * Logger is injected for testability. + */ +export async function routeInitialMessage( + id: string, + port: number, + initialPacket: ServerPacket, + log: ServerLogger, ): Promise { - // Route the initial message to the appropriate handler - // Messages may be encrypted, this will be handled by the handler - - log.debug(`Routing message for port ${port}: ${initialPacket.toHexString()}`); - let responses: SerializableInterface[] = []; - - switch (port) { - case 43300: - // Handle transactions packet - responses = ( - await receiveTransactionsData({ - connectionId: id, - message: initialPacket, - }) - ).messages; - break; - default: - console.log(`No handler found for port ${port}`); - break; - } - - // Send responses back to the client - log.debug(`[${id}] Sending ${responses.length} responses`); + log.debug( + `Routing message for port ${port}: ${initialPacket.toHexString()}`, + ); + let responses: SerializableInterface[] = []; + switch (port) { + case 43300: + responses = ( + await receiveTransactionsData({ + connectionId: id, + message: initialPacket, + }) + ).messages; + break; + default: + log.info(`[${id}] No handler found for port ${port}`); + break; + } + log.debug(`[${id}] Sending ${responses.length} responses`); + const serializedResponses = responses.map((response) => + response.serialize(), + ); + return Buffer.concat(serializedResponses); +} - // Serialize the responses - const serializedResponses = responses.map((response) => response.serialize()); +/** + * Pure function to handle incoming socket data. All dependencies are injected. + */ +export async function handleMcotsData({ + id, + port, + data, + socket, + log, + parseInitialMessageFn = parseInitialMessage, + routeInitialMessageFn = routeInitialMessage, +}: { + id: string; + port: number; + data: Buffer; + socket: NodeJS.Socket; + log: ServerLogger; + parseInitialMessageFn?: (data: Buffer) => ServerPacket; + routeInitialMessageFn?: ( + id: string, + port: number, + initialPacket: ServerPacket, + log: ServerLogger, + ) => Promise; +}) { + try { + log.debug(`[${id}] Received data: ${data.toString('hex')}`); + const initialPacket = parseInitialMessageFn(data); + log.debug(`[${id}] Initial packet(str): ${initialPacket}`); + log.debug( + `[${id}] initial Packet(hex): ${initialPacket.toHexString()}`, + ); + const response = await routeInitialMessageFn( + id, + port, + initialPacket, + log, + ); + log.debug(`[${id}] Sending response: ${response.toString('hex')}`); + socket.write(response); + } catch (error) { + Sentry.captureException(error); + log.error(`[${id}] Error handling data: ${error}`); + } +} - return Buffer.concat(serializedResponses); +/** + * Main entry point. Wires up the socket and injects dependencies. + * This is the only impure function. + */ +export async function mcotsPortRouter({ + taggedSocket, + log = getServerLogger('gateway.mcotsPortRouter'), + parseInitialMessageFn = parseInitialMessage, + routeInitialMessageFn = routeInitialMessage, +}: { + taggedSocket: TaggedSocket; + log?: ServerLogger; + parseInitialMessageFn?: (data: Buffer) => ServerPacket; + routeInitialMessageFn?: ( + id: string, + port: number, + initialPacket: ServerPacket, + log: ServerLogger, + ) => Promise; +}): Promise { + const { rawSocket: socket, connectionId: id } = taggedSocket; + const port = socket.localPort || 0; + if (port === 0) { + log.error(`[${id}] Local port is undefined`); + socket.end(); + return; + } + log.debug(`[${id}] MCOTS port router started for port ${port}`); + socket.on('data', async (data) => { + await handleMcotsData({ + id, + port, + data, + socket, + log, + parseInitialMessageFn, + routeInitialMessageFn, + }); + }); + socket.on('end', () => { + // log.debug(`[${id}] Socket closed by client for port ${port}`); + }); + socket.on('error', (error) => { + log.error(`[${id}] Socket error: ${error}`); + }); } diff --git a/packages/gateway/src/npsPortRouter.ts b/packages/gateway/src/npsPortRouter.ts index 45c94415a..66c3a0aaa 100644 --- a/packages/gateway/src/npsPortRouter.ts +++ b/packages/gateway/src/npsPortRouter.ts @@ -1,177 +1,356 @@ -import type { TaggedSocket } from "./socketUtility.js"; +import type { TaggedSocket } from './socketUtility.js'; import { - GamePacket, - type SerializableInterface, -} from "rusty-motors-shared-packets"; -import { receiveLobbyData } from "rusty-motors-lobby"; -import { receiveChatData } from "rusty-motors-chat"; -import { receivePersonaData } from "rusty-motors-personas"; -import { receiveLoginData } from "rusty-motors-login"; -import * as Sentry from "@sentry/node"; -import { getServerLogger, ServerLogger } from "rusty-motors-shared"; -import { BytableMessage } from "@rustymotors/binary"; + GamePacket, + type SerializableInterface, +} from 'rusty-motors-shared-packets'; +import { receiveLobbyData } from 'rusty-motors-lobby'; +import { receivePersonaData } from 'rusty-motors-personas'; +import { receiveLoginData } from 'rusty-motors-login'; + +import { BytableMessage, createRawMessage } from '@rustymotors/binary'; +// import { RoomServer } from '@rustymotors/roomserver'; +// import { addRoomServer, getRoomServerByPort } from 'rusty-motors-database'; +import { splitPackets } from './utility.js'; +import * as Sentry from '@sentry/node'; +import { getServerLogger, Logger, ServerLogger } from 'rusty-motors-logger'; +import { Socket } from 'net'; + +// const server01 = new RoomServer({ +// id: 224, +// name: 'MCC01', +// ip: getServerConfiguration().host, +// port: 9001, +// }); +// addRoomServer(server01); /** - * Handles routing for the NPS (Network Play System) ports. + * Routes and processes incoming socket connections for the Network Play System (NPS), handling packet parsing, multi-packet detection, and response dispatch based on the local port. + * + * Listens for data events on the provided socket, splits and parses incoming packets, routes them according to the port, and sends appropriate responses. Handles errors and logs detailed debug information throughout the process. * - * @param taggedSocket - The socket that has been tagged with additional metadata. + * @param taggedSocket - The socket connection with associated metadata to be routed. + * + * @remark If the socket's local port is undefined, the connection is closed immediately. On port 7003, an "ok to login" packet is sent upon connection. */ export async function npsPortRouter({ - taggedSocket, - log = getServerLogger("gateway.npsPortRouter"), + taggedSocket, + log = getServerLogger('gateway.npsPortRouter'), }: { - taggedSocket: TaggedSocket; - log?: ServerLogger; + taggedSocket: TaggedSocket; + log?: ServerLogger; }): Promise { - const { rawSocket: socket, connectionId: id } = taggedSocket; - - const port = socket.localPort || 0; - - if (port === 0) { - log.error(`[${id}] Local port is undefined`); - socket.end(); - return; - } - log.debug(`[${id}] NPS port router started for port ${port}`); - - if (port === 7003) { - // Sent ok to login packet - log.debug(`[${id}] Sending ok to login packet`); - socket.write(Buffer.from([0x02, 0x30, 0x00, 0x00])); - } - - // Handle the socket connection here - socket.on("data", async (data) => { - try { - log.debug(`[${id}] Received data: ${data.toString("hex")}`); - const initialPacket = parseInitialMessage(data); - log.debug(`[${id}] Initial packet(str): ${initialPacket}`); - log.debug(`[${id}] initial Packet(hex): ${initialPacket.toString()}`); - await routeInitialMessage(id, port, initialPacket) - .then((response) => { - // Send the response back to the client - log.debug( - `[${id}] Sending response to socket: ${response.toString("hex")}`, - ); - socket.write(response); - }) - .catch((error) => { - throw new Error( - `[${id}] Error routing initial nps message: ${error}`, - { - cause: error, - }, - ); - }); - } catch (error) { - if (error instanceof RangeError) { - log.warn(`[${id}] Error parsing initial nps message: ${error}`); - } else { - Sentry.captureException(error); - log.error(`[${id}] Error handling data: ${error}`); - } - } - }); - - socket.on("end", () => { - // log.debug(`[${id}] Socket closed by client for port ${port}`); - }); - - socket.on("error", (error) => { - log.error(`[${id}] Socket error: ${error}`); - }); + const { rawSocket: socket, connectionId: id } = taggedSocket; + + const port = socket.localPort || 0; + + if (port === 0) { + log.error(`[${id}] Local port is undefined`); + socket.end(); + return; + } + log.debug(`[${id}] NPS port router started for port ${port}`); + + // getMCOProtocolInstance().acceptIncomingSocket({ + // connectionId: id, + // port, + // socket, + // }); + + // return; + + // TODO: Document this + if (port === 7003) { + // Sent ok to login packet + log.debug(`[${id}] Sending ok to login packet`); + socket.write(Buffer.from([0x02, 0x30, 0x00, 0x04])); + } + + // Handle the socket connection here + socket.on('data', processSocketData(log, id, port, socket)); + + socket.on('end', () => { + // log.debug(`[${id}] Socket closed by client for port ${port}`); + }); + + socket.on('error', (error) => { + if (error.message.includes('ECONNRESET')) { + log.debug(`[${id}] Connection reset by client`); + return; + } + log.error(`[${id}] Socket error: ${error}`); + }); } /** - * Parses the initial message from a buffer and returns a `GamePacket` object. -* -* @param data - The buffer containing the initial message data. -* @returns A `GamePacket` object deserialized from the buffer. -*/ + * Processes incoming socket data, splits it into packets if necessary, and routes + * the initial message for further handling. Sends the response back to the client + * through the socket. + * + * @param log - The logger instance used for logging debug, warning, and error messages. + * @param id - A unique identifier for the current connection or session. + * @param port - The port number associated with the socket connection. + * @param socket - The socket instance used for communication with the client. + * @returns A function that processes incoming data buffers from the socket. + * + * The returned function: + * - Logs the received data and its length. + * - Splits the data into packets based on a predefined separator if multiple packets are detected. + * - Parses the initial message from each packet. + * - Routes the initial message and sends the response back to the client. + * - Handles errors during parsing, routing, or response sending, logging them appropriately. + */ +export function processSocketData( + log: Logger, + id: string, + port: number, + socket: Socket, +): (data: Buffer) => void { + return async (data) => { + try { + log.debug(`[${id}] Received data: ${data.toString('hex')}`); + log.debug(`[${id}] Data length: ${data.length}`); + + const separator = Buffer.from([0x11, 0x01]); + const packets = splitDataIntoPackets(data, separator, log, id); + + for (const packet of packets) { + const initialPacket = parseInitialMessage(packet); + log.debug(`[${id}] Initial packet(str): ${initialPacket}`); + log.debug( + `[${id}] initial Packet(hex): ${initialPacket.toString()}`, + ); + await handlePacketRouting(id, port, initialPacket, socket, log); + } + } catch (error) { + handleSocketError(error, log, id); + } + }; +} + +function splitDataIntoPackets( + data: Buffer, + separator: Buffer, + log: Logger, + id: string, +): Buffer[] { + const packetCount = + data.toString('hex').split(separator.toString('hex')).length - 1; + log.debug(`[${id}] Number of packets: ${packetCount}`); + + if (packetCount > 1) { + log.debug(`[${id}] More than one packet detected`); + const packets = splitPackets(data, separator); + log.debug( + `[${id}] Split packets: ${packets.map((p) => p.toString('hex'))}`, + ); + return packets; + } else { + log.debug(`[${id}] One packet detected`); + return [data]; + } +} + +async function handlePacketRouting( + id: string, + port: number, + initialPacket: BytableMessage, + socket: Socket, + log: Logger, +): Promise { + try { + const response = await routeInitialMessage(id, port, initialPacket); + log.debug( + `[${id}] Sending response to socket: ${response.toString('hex')}`, + ); + socket.write(response); + } catch (error) { + throw new Error(`[${id}] Error routing initial nps message: ${error}`, { + cause: error, + }); + } +} + +function handleSocketError(error: unknown, log: Logger, id: string): void { + if (error instanceof RangeError) { + log.warn(`[${id}] Error parsing initial nps message: ${error}`); + } else { + Sentry.captureException(error); + log.error(`[${id}] Error handling data: ${error}`); + } +} + +/** + * Parses a raw buffer into a `BytableMessage` representing the initial game packet. + * + * Sets the message version based on the packet ID, then deserializes the buffer into a message object. + * + * @param data - The buffer containing the raw initial message. + * @returns The parsed `BytableMessage` object. + * + * @throws {Error} If the buffer cannot be parsed into a valid message. + */ function parseInitialMessage(data: Buffer): BytableMessage { - try { - const message = new BytableMessage(0); - - // There are a few messages here that need special handling due to length - const id = data.readUInt16BE(0); - if ([0x1101, 0x217].includes(id)) { - message.setVersion(0) - } - message.setSerializeOrder([{ name: "data", field: "Raw" }]); - message.deserialize(data); - return message; - } catch (error) { - const err = new Error(`Error parsing initial message: ${error}`, { - cause: error, - }); - getServerLogger("gateway.npsPortRouter/parseInitialMessage").error( - (err as Error).message, - ); - throw err; - } + try { + const message = createRawMessage(); + message.setVersion(1); + + // There are a few messages here that need special handling due to length + const id = data.readUInt16BE(0); + if ([0x217, 0x532].includes(id)) { + message.setVersion(0); + } + getServerLogger('gateway.npsPortRouter/parseInitialMessage').debug( + `Parsing initial message: ${data.toString('hex')}`, + ); + + message.deserialize(data); + + getServerLogger('gateway.npsPortRouter/parseInitialMessage').debug( + `Parsed initial message: ${message.serialize().toString('hex')}`, + ); + return message; + } catch (error) { + const err = new Error(`Error parsing initial message: ${error}`, { + cause: error, + }); + getServerLogger('gateway.npsPortRouter/parseInitialMessage').error( + (err as Error).message, + ); + throw err; + } } /** - * Routes the initial message to the appropriate handler based on the port number. - * Handles different types of packets such as lobby data, login data, chat data, and persona data. - * Logs the routing process and the number of responses sent back to the client. + * Routes an initial game packet to the appropriate handler based on the connection port and returns the serialized response(s). + * + * Depending on the port, this function delegates processing to the lobby, login, or persona data handlers, and aggregates their responses for the client. + * + * @param connectionId - Unique identifier for the client connection. + * @param connectionPort - Local port number used to select the appropriate handler. + * @param initialPacket - The parsed initial packet received from the client. + * @param log - Optional logger for debug and warning messages. + * @returns A buffer containing the concatenated serialized responses from the selected handler. * - * @param id - The connection ID of the client. - * @param port - The port number to determine the type of packet. - * @param initialPacket - The initial packet received from the client. - * @param log - The logger to use for logging messages. - * @returns A promise that resolves to a Buffer containing the serialized responses. + * @remark If no handler exists for the specified {@link connectionPort}, an empty buffer is returned. */ async function routeInitialMessage( - id: string, - port: number, - initialPacket: BytableMessage, - log = getServerLogger("gateway.npsPortRouter/routeInitialMessage"), + connectionId: string, + connectionPort: number, + initialPacket: BytableMessage, + log = getServerLogger('gateway.npsPortRouter/routeInitialMessage'), ): Promise { - // Route the initial message to the appropriate handler - // Messages may be encrypted, this will be handled by the handler - - log.debug(`Routing message for port ${port}: ${initialPacket.toString()}`); - - const packet = new GamePacket(); - packet.deserialize(initialPacket.serialize()); - - let responses: SerializableInterface[] = []; - - switch (port) { - case 7003: - responses = ( - await receiveLobbyData({ connectionId: id, message: packet }) - ).messages; - break; - case 8226: - // Handle login packet - responses = ( - await receiveLoginData({ connectionId: id, message: initialPacket }) - ).messages; - break; - case 8227: - // Handle chat packet - responses = (await receiveChatData({ connectionId: id, message: packet })) - .messages; - break; - case 8228: - // responses =Handle persona packet - responses = ( - await receivePersonaData({ connectionId: id, message: packet }) - ).messages; - break; - default: - console.log(`No handler found for port ${port}`); - break; - } - - // Send responses back to the client - log.debug(`[${id}] Sending ${responses.length} responses`); - - // Serialize the responses - const serializedResponses = responses.map((response) => response.serialize()); - - return Buffer.concat(serializedResponses); + // Route the initial message to the appropriate handler + // Messages may be encrypted, this will be handled by the handler + + log.debug( + `Routing message for port ${connectionPort}: ${initialPacket.toString()}`, + ); + + const gameRequestPacket = new GamePacket(); + gameRequestPacket.deserialize(initialPacket.serialize()); + + let packetResponses: SerializableInterface[] = []; + + switch (connectionPort) { + case 7003: + // Handle lobby packet + log.debug( + `[${connectionId}] Passing packet to lobby handler: ${gameRequestPacket.serialize().toString('hex')}`, + ); + packetResponses = ( + await receiveLobbyData({ + connectionId: connectionId, + message: initialPacket, + }) + ).messages; + log.debug( + `[${connectionId}] Lobby Responses: ${packetResponses.map((r) => r.serialize().toString('hex'))}`, + ); + break; + case 8226: + // Handle login packet + log.debug( + `[${connectionId}] Passing packet to login handler: ${gameRequestPacket.serialize().toString('hex')}`, + ); + packetResponses = ( + await receiveLoginData({ + connectionId: connectionId, + message: initialPacket, + }) + ).messages; + log.debug( + `[${connectionId}] Login Responses: ${packetResponses.map((r) => r.serialize().toString('hex'))}`, + ); + break; + // case 8227: + // // Handle chat packet + // log.debug( + // `[${id}] Passing packet to chat handler: ${packet.serialize().toString("hex")}`, + // ); + // responses = (await receiveChatData({ connectionId: id, message: packet })) + // .messages; + // log.debug(`[${id}] Chat Responses: ${responses.map((r) => r.serialize().toString("hex"))}`); + // break; + case 8228: + log.debug( + `[${connectionId}] Passing packet to persona handler: ${gameRequestPacket.serialize().toString('hex')}`, + ); + // responses =Handle persona packet + packetResponses = ( + await receivePersonaData({ + connectionId: connectionId, + message: gameRequestPacket, + }) + ).messages; + log.debug( + `[${connectionId}] Persona Responses: ${packetResponses.map((r) => r.serialize().toString('hex'))}`, + ); + break; + // case 9001: { + // const roomServer = getRoomServerByPort(connectionPort); + // if (roomServer === undefined) { + // log.warn( + // `No room server found for port ${connectionPort}`, + // ); + // break; + // } + // log.debug( + // `Passing packet to room server: ${roomServer.name}`, + // ); + // packetResponses = ( + // await roomServer.receivePacket({ + // connectionId: connectionId, + // packet: initialPacket, + // }) + // ).messages; + // const serializedResponseData = Buffer.concat( + // packetResponses.map((r) => r.serialize()), + // ); + // log.debug( + // { + // id: connectionId, + // port: connectionPort, + // roomServer: roomServer.name, + // responseBuffer: serializedResponseData.toString('hex'), + // }, + // `Room Server Responses`, + // ); + // break; + // } + default: + // No handler + log.warn(`No handler found for port ${connectionPort}`); + break; + } + + // Send responses back to the client + log.debug(`[${connectionId}] Sending ${packetResponses.length} responses`); + + // Serialize the responses + const serializedResponses = packetResponses.map((response) => + response.serialize(), + ); + + return Buffer.concat(serializedResponses); } diff --git a/packages/gateway/src/portRouters.ts b/packages/gateway/src/portRouters.ts index 9641b29a4..377931404 100644 --- a/packages/gateway/src/portRouters.ts +++ b/packages/gateway/src/portRouters.ts @@ -1,5 +1,5 @@ -import type { PortRouter, PortRouterArgs } from "./types.js"; -import { getServerLogger } from "rusty-motors-shared"; +import type { PortRouter, PortRouterArgs } from './types.js'; +import { getServerLogger } from 'rusty-motors-logger'; /** * A map that associates port numbers with their corresponding router functions. @@ -15,10 +15,10 @@ const portRouters = new Map(); */ export function addPortRouter(port: number, router: PortRouter) { - if (!Number.isInteger(port) || port < 0 || port > 65535) { - throw new Error(`Invalid port number: ${port}`); - } - portRouters.set(port, router); + if (!Number.isInteger(port) || port < 0 || port > 65535) { + throw new Error(`Invalid port number: ${port}`); + } + portRouters.set(port, router); } /** * Handles the case where no router is found for the given socket. @@ -31,16 +31,16 @@ export function addPortRouter(port: number, router: PortRouter) { */ async function notFoundRouter({ - taggedSocket, - log = getServerLogger("gateway.notFoundRouter"), + taggedSocket, + log = getServerLogger('gateway.notFoundRouter'), }: PortRouterArgs) { - taggedSocket.rawSocket.on("error", (error) => { - console.error(`[${taggedSocket.connectionId}] Socket error: ${error}`); - }); - taggedSocket.rawSocket.end(); - log.warn( - `[${taggedSocket.connectionId}] No router found for port ${taggedSocket.rawSocket.localPort}`, - ); + taggedSocket.rawSocket.on('error', (error) => { + console.error(`[${taggedSocket.connectionId}] Socket error: ${error}`); + }); + taggedSocket.rawSocket.end(); + log.warn( + `[${taggedSocket.connectionId}] No router found for port ${taggedSocket.rawSocket.localPort}`, + ); } /** * Retrieves the router function associated with a given port. @@ -51,14 +51,14 @@ async function notFoundRouter({ */ export function getPortRouter(port: number): PortRouter { - if (!Number.isInteger(port) || port < 0 || port > 65535) { - throw new Error(`Invalid port number: ${port}`); - } - const router = portRouters.get(port); - if (typeof router === "undefined") { - return notFoundRouter; - } - return router; + if (!Number.isInteger(port) || port < 0 || port > 65535) { + throw new Error(`Invalid port number: ${port}`); + } + const router = portRouters.get(port); + if (typeof router === 'undefined') { + return notFoundRouter; + } + return router; } /** @@ -68,5 +68,5 @@ export function getPortRouter(port: number): PortRouter { * effectively resetting it to an empty state. */ export function clearPortRouters() { - portRouters.clear(); + portRouters.clear(); } diff --git a/packages/gateway/src/types.ts b/packages/gateway/src/types.ts index cfe07e521..7b6c4add8 100644 --- a/packages/gateway/src/types.ts +++ b/packages/gateway/src/types.ts @@ -1,21 +1,25 @@ -import type { TaggedSocket } from "./socketUtility.js"; -import type { Configuration, ServerLogger } from "rusty-motors-shared"; -import type { Socket } from "node:net"; +import type { TaggedSocket } from './socketUtility.js'; +import type { Configuration } from 'rusty-motors-shared'; +import type { Socket } from 'node:net'; +import { ServerLogger } from 'rusty-motors-logger'; export type PortRouterArgs = { - taggedSocket: TaggedSocket; - log?: ServerLogger; + taggedSocket: TaggedSocket; + log?: ServerLogger; }; export type PortRouter = (portRouterArgs: PortRouterArgs) => Promise; export interface GatewayOptions { - config?: Configuration; - log?: ServerLogger; - backlogAllowedCount?: number; - listeningPortList?: number[]; - socketConnectionHandler?: ({ - incomingSocket, - log, - }: { incomingSocket: Socket; log?: ServerLogger }) => void; + config?: Configuration; + log?: ServerLogger; + backlogAllowedCount?: number; + listeningPortList?: number[]; + socketConnectionHandler?: ({ + incomingSocket, + log, + }: { + incomingSocket: Socket; + log?: ServerLogger; + }) => void; } diff --git a/packages/gateway/src/utility.test.ts b/packages/gateway/src/utility.test.ts new file mode 100644 index 000000000..9e528e7a1 --- /dev/null +++ b/packages/gateway/src/utility.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from 'vitest'; +import { splitPackets } from './utility.js'; + +const buf = (str: string) => Buffer.from(str); + +describe('splitPackets', () => { + it('returns the entire buffer as a single packet if no separator is found', () => { + expect(splitPackets(buf('abcdef'), buf('xyz'))).toEqual([buf('abcdef')]); + }); + + it('splits the buffer into packets using the separator', () => { + expect(splitPackets(buf('abc|def|ghi'), buf('|'))).toEqual([ + buf('abc'), + buf('def'), + buf('ghi'), + ]); + expect(splitPackets(buf('abc--def--ghi'), buf('--'))).toEqual([ + buf('abc'), + buf('def'), + buf('ghi'), + ]); + expect(splitPackets(buf('abc|def|ghi|jkl'), buf('|'))).toEqual([ + buf('abc'), + buf('def'), + buf('ghi'), + buf('jkl'), + ]); + }); + + it('throws an error for invalid separator positions or consecutive separators', () => { + expect(() => splitPackets(buf('abc|def|'), buf('|'))).toThrow( + 'Separator found at the end of the buffer', + ); + expect(() => splitPackets(buf('abc||def'), buf('|'))).toThrow( + 'Multiple consecutive separators found', + ); + }); + + it('returns an empty array for empty data', () => { + expect(splitPackets(buf(''), buf('|'))).toEqual([]); + }); +}); diff --git a/packages/gateway/src/utility.ts b/packages/gateway/src/utility.ts new file mode 100644 index 000000000..8b3bbe96c --- /dev/null +++ b/packages/gateway/src/utility.ts @@ -0,0 +1,47 @@ +/** + * Splits a buffer into packets using a specified separator buffer. + * + * Searches for the separator starting from the third byte of {@link data}. + * If no separator is found, returns the entire buffer as a single packet. + * Each packet starts 3 bytes before the separator and ends 3 bytes before the next separator. + * + * @param data - The buffer to split into packets. + * @param separator - The buffer used as the separator between packets. + * @returns An array of buffer packets split by the separator. Each packet stars 3 bytes before the separator and ends 3 bytes before the next separator. + * + * @throws {Error} If the separator is found at the end of the buffer + * @throws {Error} If multiple consecutive separators are found + * @throws {Error} If the separator is not found at the start or end of the buffer + * @throws {Error} If the separator is longer than one character + */ +export function splitPackets(data: Buffer, separator: Buffer): Buffer[] { + if (data.length === 0) { + return []; + } + + if (separator.length === 0) { + throw new Error('Separator cannot be empty'); + } + + // Check if the buffer ends with the separator + if (data.slice(-separator.length).equals(separator)) { + throw new Error('Separator found at the end of the buffer'); + } + + const result: Buffer[] = []; + let start = 0; + let index; + + while ((index = data.indexOf(separator, start)) !== -1) { + // Check for multiple consecutive separators + if (index === start) { + throw new Error('Multiple consecutive separators found'); + } + + result.push(data.slice(start, index)); + start = index + separator.length; + } + + result.push(data.slice(start)); + return result; +} diff --git a/packages/gateway/src/web.test.ts b/packages/gateway/src/web.test.ts index 8e7802c0b..986fd116a 100644 --- a/packages/gateway/src/web.test.ts +++ b/packages/gateway/src/web.test.ts @@ -1,100 +1,112 @@ -import { describe, it, expect, vi } from "vitest"; -import http from "node:http"; -import { processHttpRequest, initializeRouteHandlers } from "./web"; -import { before } from "node:test"; - -describe("processHttpRequest", () => { - before(() => { - initializeRouteHandlers(); - }); - - it("should respond with 'Hello, world!' for the root path", () => { - const request = { - url: "/", - } as http.IncomingMessage; - - const response = { - setHeader: vi.fn(), - end: vi.fn(), - } as unknown as http.ServerResponse; - - processHttpRequest(request, response); - - expect(response.setHeader).toHaveBeenCalledWith( - "Content-Type", - "text/plain", - ); - expect(response.end).toHaveBeenCalledWith("Hello, world!"); - }); - - it("should respond with 404 for unknown paths", () => { - const request = { - url: "/unknown", - } as http.IncomingMessage; - - const response = { - setHeader: vi.fn(), - end: vi.fn(), - statusCode: 0, - } as unknown as http.ServerResponse; - - processHttpRequest(request, response); - - expect(response.statusCode).toBe(404); - expect(response.end).toHaveBeenCalledWith("Not found"); - }); - - it("should handle /AuthLogin path", () => { - const request = { - url: "/AuthLogin?username=new&password=new", - } as http.IncomingMessage; - - const response = { - setHeader: vi.fn(), - end: vi.fn(), - } as unknown as http.ServerResponse; - - processHttpRequest(request, response); - - expect(response.setHeader).toHaveBeenCalledWith( - "Content-Type", - "text/plain", - ); - expect(response.end).toHaveBeenCalledWith( - expect.stringContaining("Valid=TRUE"), - ); - }); - - it("should handle /ShardList/ path", () => { - const request = { - url: "/ShardList/", - } as http.IncomingMessage; - - const response = { - setHeader: vi.fn(), - end: vi.fn(), - } as unknown as http.ServerResponse; +import { describe, it, expect, vi } from 'vitest'; +import { IncomingMessage, ServerResponse } from 'node:http'; +import { processHttpRequest, initializeRouteHandlers } from './web.js'; +import { databaseService } from 'rusty-motors-database'; +import { before } from 'node:test'; + +describe('processHttpRequest', () => { + before(() => { + initializeRouteHandlers(); + }); + + it("should respond with 'Hello, world!' for the root path", async () => { + const request = { + url: '/', + } as IncomingMessage; + + const response = { + setHeader: vi.fn(), + end: vi.fn(), + } as unknown as ServerResponse; + + await processHttpRequest(request, response); + + expect(response.setHeader).toHaveBeenCalledWith( + 'Content-Type', + 'text/plain', + ); + expect(response.end).toHaveBeenCalledWith('Hello, world!'); + }); + + it('should respond with 404 for unknown paths', async () => { + const request = { + url: '/unknown', + } as IncomingMessage; + + const response = { + setHeader: vi.fn(), + end: vi.fn(), + statusCode: 0, + } as unknown as ServerResponse; + + await processHttpRequest(request, response); + + expect(response.statusCode).toBe(404); + expect(response.end).toHaveBeenCalledWith('Not found'); + }); + + it('should handle /AuthLogin path', async () => { + // Mock the databaseService methods for this test + const mockUser = { username: 'new', ticket: 'ticket123', customerId: '1' }; + const originalRetrieveUserAccountAsync = databaseService.retrieveUserAccountAsync; + const originalGenerateTicketAsync = databaseService.generateTicketAsync; + databaseService.retrieveUserAccountAsync = vi.fn().mockResolvedValue(mockUser); + databaseService.generateTicketAsync = vi.fn().mockResolvedValue('ticket123'); + + const request = { + url: '/AuthLogin?username=new&password=new', + } as IncomingMessage; + + const response = { + setHeader: vi.fn(), + end: vi.fn(), + } as unknown as ServerResponse; + + await processHttpRequest(request, response); + + expect(response.setHeader).toHaveBeenCalledWith( + 'Content-Type', + 'text/plain', + ); + expect(response.end).toHaveBeenCalledWith( + expect.stringContaining('Valid=TRUE'), + ); + + // Restore original methods + databaseService.retrieveUserAccountAsync = originalRetrieveUserAccountAsync; + databaseService.generateTicketAsync = originalGenerateTicketAsync; + }); + + it('should handle /ShardList/ path', async () => { + const request = { + url: '/ShardList/', + } as IncomingMessage; + + const response = { + setHeader: vi.fn(), + end: vi.fn(), + } as unknown as ServerResponse; const originalEnv = process.env; const testEnv = { - EXTERNAL_HOST: "localhost", - CERTIFICATE_FILE: "cert.pem", - PRIVATE_KEY_FILE: "key.pem", - PUBLIC_KEY_FILE: "public.pem", - LOG_LEVEL: "info", + EXTERNAL_HOST: 'localhost', + CERTIFICATE_FILE: 'cert.pem', + PRIVATE_KEY_FILE: 'key.pem', + PUBLIC_KEY_FILE: 'public.pem', + LOG_LEVEL: 'info', }; process.env = { ...originalEnv, ...testEnv, - } + }; - processHttpRequest(request, response); + await processHttpRequest(request, response); - expect(response.setHeader).toHaveBeenCalledWith( - "Content-Type", - "text/plain", - ); - expect(response.end).toHaveBeenCalledWith(expect.any(String)); - }); -}); \ No newline at end of file + expect(response.setHeader).toHaveBeenCalledWith( + 'Content-Type', + 'text/plain', + ); + expect(response.end).toHaveBeenCalledWith(expect.any(String)); + }); +}); diff --git a/packages/gateway/src/web.ts b/packages/gateway/src/web.ts index d21fcc733..d4e5bb047 100644 --- a/packages/gateway/src/web.ts +++ b/packages/gateway/src/web.ts @@ -14,88 +14,100 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import http from "node:http"; -import { CastanetResponse } from "./CastanetResponse"; -import { generateShardList } from "rusty-motors-shard"; +import http from 'node:http'; +import { CastanetResponse } from './CastanetResponse.js'; +import { generateShardList } from 'rusty-motors-shard'; import { - handleGetCert, - handleGetKey, - handleGetRegistry, -} from "rusty-motors-shard"; -import { getServerConfiguration } from "rusty-motors-shared"; + handleGetCert, + handleGetKey, + handleGetRegistry, +} from 'rusty-motors-shard'; +import { getServerConfiguration } from 'rusty-motors-shared'; +import { databaseService } from 'rusty-motors-database'; type WebHandlerResponse = { - headers: Record; - body: string | Buffer; + headers: Record; + body: string | Buffer; }; -type WebHandler = (request: http.IncomingMessage, response: http.ServerResponse) => WebHandlerResponse; +type WebHandler = ( + request: http.IncomingMessage, + response: http.ServerResponse, +) => WebHandlerResponse | Promise; class AuthLoginResponse { - valid: boolean = false; - ticket: string = ""; - reasonCode: string = ""; - reasonText: string = ""; - reasonUrl: string = ""; - - static createValid(ticket: string) { - const response = new AuthLoginResponse(); - response.valid = true; - response.ticket = ticket; - return response; - } - - static createInvalid( - reasonCode: string, - reasonText: string, - reasonUrl: string, - ) { - const response = new AuthLoginResponse(); - response.valid = false; - response.reasonCode = reasonCode; - response.reasonText = reasonText; - response.reasonUrl = reasonUrl; - return response; - } - - formatResponse() { - if (this.valid) { - return `Valid=TRUE\nTicket=${this.ticket}`; - } else { - return `reasoncode=${this.reasonCode}\nreasontext=${this.reasonText}\nreasonurl=${this.reasonUrl}`; - } - } + valid: boolean = false; + ticket: string = ''; + reasonCode: string = ''; + reasonText: string = ''; + reasonUrl: string = ''; + + static createValid(ticket: string) { + const response = new AuthLoginResponse(); + response.valid = true; + response.ticket = ticket; + return response; + } + + static createInvalid( + reasonCode: string, + reasonText: string, + reasonUrl: string, + ) { + const response = new AuthLoginResponse(); + response.valid = false; + response.reasonCode = reasonCode; + response.reasonText = reasonText; + response.reasonUrl = reasonUrl; + return response; + } + + formatResponse() { + if (this.valid) { + return `Valid=TRUE\nTicket=${this.ticket}`; + } else { + return `reasoncode=${this.reasonCode}\nreasontext=${this.reasonText}\nreasonurl=${this.reasonUrl}`; + } + } } - const routeHandlers: Map = new Map(); export function initializeRouteHandlers() { - routeHandlers.set("/", handleRoot); - routeHandlers.set("/games/EA_Seattle/MotorCity/UpdateInfo", handleCastanet); - routeHandlers.set("/games/EA_Seattle/MotorCity/NPS", handleCastanet); - routeHandlers.set("/games/EA_Seattle/MotorCity/MCO", handleCastanet); - routeHandlers.set("/AuthLogin", handleAuthLogin); - routeHandlers.set("/ShardList/", handleShardList); - routeHandlers.set("/ticker", handleTicker); - routeHandlers.set("/cert", () => { - return { - headers: { "Content-Type": "octet-stream", "Content-Disposition": "attachment; filename=server.crt" }, - body: handleGetCert(getServerConfiguration()), - }; - }); - routeHandlers.set("/key", () => { - return { - headers: { "Content-Type": "octet-stream", "Content-Disposition": "attachment; filename=pub.key" }, - body: handleGetKey(getServerConfiguration()), - }; - }); - routeHandlers.set("/registry", () => { - return { - headers: { "Content-Type": "octet-stream", "Content-Disposition": "attachment; filename=server.reg" }, - body: handleGetRegistry(getServerConfiguration()), - }; - }); + routeHandlers.set('/', handleRoot); + routeHandlers.set('/games/EA_Seattle/MotorCity/UpdateInfo', handleCastanet); + routeHandlers.set('/games/EA_Seattle/MotorCity/NPS', handleCastanet); + routeHandlers.set('/games/EA_Seattle/MotorCity/MCO', handleCastanet); + routeHandlers.set('/AuthLogin', handleAuthLogin); + routeHandlers.set('/ShardList/', handleShardList); + routeHandlers.set('/ticker', handleTicker); + routeHandlers.set('/cert', (_req, _res) => { + return { + headers: { + 'Content-Type': 'octet-stream', + 'Content-Disposition': 'attachment; filename=server.crt', + }, + body: handleGetCert(getServerConfiguration()), + }; + }); + routeHandlers.set('/key', (_req, _res) => { + return { + headers: { + 'Content-Type': 'octet-stream', + 'Content-Disposition': 'attachment; filename=pub.key', + }, + body: handleGetKey(getServerConfiguration()), + }; + }); + routeHandlers.set('/registry', (_req, _res) => { + return { + headers: { + 'Content-Type': 'octet-stream', + 'Content-Disposition': 'attachment; filename=server.reg', + }, + body: handleGetRegistry(getServerConfiguration()), + }; + }); } /** @@ -104,10 +116,10 @@ export function initializeRouteHandlers() { * @returns The response headers and body for the root path request. */ function handleRoot(): WebHandlerResponse { - return { - headers: {"Content-Type": "text/plain"}, - body: "Hello, world!", - }; + return { + headers: { 'Content-Type': 'text/plain' }, + body: 'Hello, world!', + }; } /** @@ -116,12 +128,12 @@ function handleRoot(): WebHandlerResponse { * @returns The response headers and body for Castanet routes. */ function handleCastanet(): WebHandlerResponse { - return { - headers: { - [CastanetResponse.header.type]: CastanetResponse.header.value, - }, - body: CastanetResponse.body, - }; + return { + headers: { + [CastanetResponse.header.type]: CastanetResponse.header.value, + }, + body: CastanetResponse.body, + }; } /** @@ -130,15 +142,13 @@ function handleCastanet(): WebHandlerResponse { * @returns The response headers and body for the ticker request. */ function handleTicker(): WebHandlerResponse { - return { - headers: {"Content-Type": "text/plain"}, - body: `/color=0xFFFF00 - Hi Mark!` - }; + return { + headers: { 'Content-Type': 'text/plain' }, + body: `/color=0xFFFF00 + Hi Mark!`, + }; } - - /** * Handles the authentication login process. * @@ -149,37 +159,37 @@ function handleTicker(): WebHandlerResponse { * @param request - The incoming HTTP request object. * @param response - The HTTP response object to send the authentication response. */ -function handleAuthLogin( - request: http.IncomingMessage, - response: http.ServerResponse, -): WebHandlerResponse { - const url = new URL( - `http://${process.env["HOST"] ?? "localhost"}${request.url}`, - ); - const username = url.searchParams.get("username") ?? ""; - const password = url.searchParams.get("password") ?? ""; - - response.setHeader("Content-Type", "text/plain"); - let authResponse: AuthLoginResponse; - authResponse = AuthLoginResponse.createInvalid( - "INV-100", - "Opps!", - "https://winehq.com", - ); - - const user = retrieveUserAccount(username, password); - - if (user !== null) { - const ticket = generateTicket(user.customerId); - if (ticket !== "") { - authResponse = AuthLoginResponse.createValid(ticket); - } - } - - return { - headers: {"Content-Type": "text/plain"}, - body: authResponse.formatResponse(), - } +async function handleAuthLogin( + request: http.IncomingMessage, + response: http.ServerResponse, +): Promise { + const url = new URL( + `http://${process.env['HOST'] ?? 'localhost'}${request.url}`, + ); + const username = url.searchParams.get('username') ?? ''; + const password = url.searchParams.get('password') ?? ''; + + response.setHeader('Content-Type', 'text/plain'); + let authResponse: AuthLoginResponse; + authResponse = AuthLoginResponse.createInvalid( + 'INV-100', + 'Opps!', + 'https://winehq.com', + ); + + const user = await databaseService.retrieveUserAccountAsync(username, password); + + if (user !== null) { + const ticket = await databaseService.generateTicketAsync(user.customerId); + if (ticket !== '') { + authResponse = AuthLoginResponse.createValid(ticket); + } + } + + return { + headers: { 'Content-Type': 'text/plain' }, + body: authResponse.formatResponse(), + }; } /** @@ -188,16 +198,13 @@ function handleAuthLogin( * @returns The response headers and body for the shard list request. */ function handleShardList(): WebHandlerResponse { - const shardList = generateShardList(getServerConfiguration().host); - return { - headers: {"Content-Type": "text/plain"}, - body: shardList, - }; + const shardList = generateShardList(getServerConfiguration().host); + return { + headers: { 'Content-Type': 'text/plain' }, + body: shardList, + }; } - - - /** * Handles incoming HTTP requests and sends appropriate responses based on the request URL. * @@ -214,87 +221,28 @@ function handleShardList(): WebHandlerResponse { * - `/registry`: Responds with registry information based on server configuration. * - Any other route: Responds with a 404 status code and "Not found" message. */ -export function processHttpRequest( - request: http.IncomingMessage, - response: http.ServerResponse, +export async function processHttpRequest( + request: http.IncomingMessage, + response: http.ServerResponse, ) { - - const url = new URL( - `http://${process.env["HOST"] ?? "localhost"}${request.url}`, - ); - - if (routeHandlers.has(url.pathname)) { - const handler = routeHandlers.get(url.pathname); - if (handler) { - const { headers, body } = handler(request, response); - Object.entries(headers).forEach(([key, value]) => { - response.setHeader(key, value); - }); - response.end(body); - return; - } - } - - response.statusCode = 404; - response.end("Not found"); -} - - - -const UserAccounts = [ - { - username: "new", - ticket: "5213dee3a6bcdb133373b2d4f3b9962758", - password: "new", - customerId: "123456", - }, - { - username: "admin", - ticket: "d316cd2dd6bf870893dfbaaf17f965884e", - password: "admin", - customerId: "654321", - }, -]; - -const AuthTickets = [ - { - ticket: "5213dee3a6bcdb133373b2d4f3b9962758", - customerId: "123456", - }, - { - ticket: "d316cd2dd6bf870893dfbaaf17f965884e", - customerId: "654321", - }, -]; - -/** - * Generates a ticket for the given customer ID. - * - * @param customerId - The ID of the customer for whom the ticket is being generated. - * @returns The ticket associated with the given customer ID, or an empty string if no ticket is found. - */ -function generateTicket(customerId: string): string { - const ticket = AuthTickets.find((t) => t.customerId === customerId); - if (ticket) { - return ticket.ticket; - } - return ""; -} - -/** - * Retrieves a user account based on the provided username and password. - * - * @param username - The username of the account to retrieve. - * @param password - The password of the account to retrieve. - * @returns An object containing the username, ticket, and customerId if the account is found, or null if not. - */ -function retrieveUserAccount( - username: string, - password: string, -): { username: string; ticket: string; customerId: string } | null { - const customer = UserAccounts.find( - (account) => account.username === username && account.password === password, - ); - - return customer ?? null; + const url = new URL( + `http://${process.env['HOST'] ?? 'localhost'}${request.url}`, + ); + + if (routeHandlers.has(url.pathname)) { + const handler = routeHandlers.get(url.pathname); + if (handler) { + // Await the handler in case it's async + const result = await handler(request, response); + const { headers, body } = result; + Object.entries(headers).forEach(([key, value]) => { + response.setHeader(key, value); + }); + response.end(body); + return; + } + } + + response.statusCode = 404; + response.end('Not found'); } diff --git a/packages/gateway/test/mcotsPortRouter.test.ts b/packages/gateway/test/mcotsPortRouter.test.ts index 84c82d42e..80213050e 100644 --- a/packages/gateway/test/mcotsPortRouter.test.ts +++ b/packages/gateway/test/mcotsPortRouter.test.ts @@ -1,121 +1,122 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { mcotsPortRouter } from "../src/mcotsPortRouter.js"; -import type { TaggedSocket } from "../src/socketUtility.js"; -import { ServerPacket } from "rusty-motors-shared-packets"; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { mcotsPortRouter } from '../src/mcotsPortRouter.js'; +import type { TaggedSocket } from '../src/socketUtility.js'; +import { ServerPacket } from 'rusty-motors-shared-packets'; -vi.mock("rusty-motors-database", () => ({ - databaseManager: { - updateSessionKey: vi.fn(), - fetchSessionKeyByConnectionId: vi.fn(), - fetchSessionKeyByCustomerId: vi.fn(), - updateUser: vi.fn(), - }, +vi.mock('rusty-motors-database', () => ({ + databaseManager: { + updateSessionKey: vi.fn(), + fetchSessionKeyByConnectionId: vi.fn(), + fetchSessionKeyByCustomerId: vi.fn(), + updateUser: vi.fn(), + }, })); -vi.mocked( - await import("rusty-motors-database"), -).databaseManager; +vi.mocked(await import('rusty-motors-database')).databaseManager; -describe("mcotsPortRouter", () => { - beforeEach(() => { - vi.resetAllMocks(); - }); - - it("should log an error and close the socket if local port is undefined", async () => { - const mockSocket = { - localPort: undefined, - end: vi.fn(), - on: vi.fn(), - }; - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket, - connectionId: "test-id", - }; - -try { - await mcotsPortRouter({ taggedSocket }); - -} catch (error) { - expect(error).toBeUndefined(); -} expect(mockSocket.end).toHaveBeenCalled(); - }); - - it("should handle data event and route initial message", async () => { - const mockSocket = { - localPort: 43300, - write: vi.fn(), - on: vi.fn((event, callback) => { - if (event === "data") { - callback(Buffer.from([0x74, 0x65, 0x73, 0x74, 0x2d, 0x64, 0x61, 0x74, 0x61])); - } - }), - }; - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket, - connectionId: "test-id-mcots", - }; - - const mockServerPacket = { - deserialize: vi.fn(), - toHexString: vi.fn().mockReturnValue("746573742d64617461"), - }; - vi.spyOn(ServerPacket.prototype, "deserialize").mockImplementation( - mockServerPacket.deserialize, - ); - vi.spyOn(ServerPacket.prototype, "toHexString").mockImplementation( - mockServerPacket.toHexString, - ); - - try { - await mcotsPortRouter({ taggedSocket }); - } catch (error) { - expect(error).toBeUndefined(); - } - }); - - it("should log socket end event", async () => { - const mockSocket = { - localPort: 43300, - on: vi.fn((event, callback) => { - if (event === "end") { - callback(); - } - }), - }; - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket, - connectionId: "test-id", - }; +const createMockSocket = (overrides: Partial = {}) => ({ + localPort: 43300, + end: vi.fn(), + on: vi.fn(), + write: vi.fn(), + destroySoon: vi.fn(), + connect: vi.fn(), + setEncoding: vi.fn(), + pause: vi.fn(), + resume: vi.fn(), + setTimeout: vi.fn(), + setNoDelay: vi.fn(), + setKeepAlive: vi.fn(), + address: vi.fn(), + unref: vi.fn(), + ref: vi.fn(), + ...overrides, +}); - try { - await mcotsPortRouter({ taggedSocket }); - } catch (error) { - expect(error).toBeUndefined(); - } - }); +const createTaggedSocket = ( + socketOverrides: Partial = {}, + id = 'test-id', +) => ({ + rawSocket: createMockSocket(socketOverrides) as any, + connectionId: id, + connectedAt: Date.now(), +}); - it("should log socket error event", async () => { - const mockSocket = { - localPort: 43300, - on: vi.fn((event, callback) => { - if (event === "error") { - callback(new Error("test-error")); - } - }), - }; +describe('mcotsPortRouter', () => { + let mockLogger: any; + beforeEach(() => { + vi.resetAllMocks(); + mockLogger = { + debug: vi.fn(), + error: vi.fn(), + info: vi.fn(), + }; + }); - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket, - connectionId: "test-id", - }; + it('should log an error and close the socket if local port is undefined', async () => { + const taggedSocket: TaggedSocket = createTaggedSocket({ + localPort: undefined, + }); + await mcotsPortRouter({ taggedSocket, log: mockLogger }); + expect(taggedSocket.rawSocket.end).toHaveBeenCalled(); + expect(mockLogger.error).toHaveBeenCalledWith( + '[test-id] Local port is undefined', + ); + }); -try { - await mcotsPortRouter({ taggedSocket}); -} catch (error) { - expect(error).toBeUndefined(); - -} + it('should handle data event and route initial message', async () => { + const on = vi.fn((event, callback) => { + if (event === 'data') { + callback( + Buffer.from([ + 0x74, 0x65, 0x73, 0x74, 0x2d, 0x64, 0x61, 0x74, 0x61, + ]), + ); + } + }); + const taggedSocket: TaggedSocket = createTaggedSocket( + { + on, + }, + 'test-id-mcots', + ); + const mockServerPacket = { + deserialize: vi.fn(), + toHexString: vi.fn().mockReturnValue('746573742d64617461'), + }; + vi.spyOn(ServerPacket.prototype, 'deserialize').mockImplementation( + mockServerPacket.deserialize, + ); + vi.spyOn(ServerPacket.prototype, 'toHexString').mockImplementation( + mockServerPacket.toHexString, + ); + await mcotsPortRouter({ taggedSocket, log: mockLogger }); + expect(mockLogger.debug).toHaveBeenCalledWith( + expect.stringContaining('MCOTS port router started for port 43300'), + ); + }); + it('should log socket end event', async () => { + const on = vi.fn((event, callback) => { + if (event === 'end') { + callback(); + } + }); + const taggedSocket: TaggedSocket = createTaggedSocket({ on }); + await mcotsPortRouter({ taggedSocket, log: mockLogger }); + // The end event is currently commented out, so no log assertion here + }); - }); + it('should log socket error event', async () => { + const on = vi.fn((event, callback) => { + if (event === 'error') { + callback(new Error('test-error')); + } + }); + const taggedSocket: TaggedSocket = createTaggedSocket({ on }); + await mcotsPortRouter({ taggedSocket, log: mockLogger }); + expect(mockLogger.error).toHaveBeenCalledWith( + expect.stringContaining('Socket error: Error: test-error'), + ); + }); }); diff --git a/packages/gateway/test/npsPortRouter.test.ts b/packages/gateway/test/npsPortRouter.test.ts index b55c3882c..db7f7a4dd 100644 --- a/packages/gateway/test/npsPortRouter.test.ts +++ b/packages/gateway/test/npsPortRouter.test.ts @@ -1,142 +1,139 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { npsPortRouter } from "../src/npsPortRouter.js"; -import type { TaggedSocket } from "../src/socketUtility.js"; -import { GamePacket } from "rusty-motors-shared-packets"; -import { ServerLogger } from "rusty-motors-shared"; -import { Socket } from "node:net"; - -vi.mock("rusty-motors-database", () => ({ - databaseManager: { - updateSessionKey: vi.fn(), - fetchSessionKeyByConnectionId: vi.fn(), - fetchSessionKeyByCustomerId: vi.fn(), - updateUser: vi.fn(), - }, +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { npsPortRouter } from '../src/npsPortRouter.js'; +import type { TaggedSocket } from '../src/socketUtility.js'; +import { GamePacket } from 'rusty-motors-shared-packets'; +import { Socket } from 'node:net'; +import { ServerLogger } from 'rusty-motors-logger'; + +vi.mock('rusty-motors-database', () => ({ + databaseManager: { + updateSessionKey: vi.fn(), + fetchSessionKeyByConnectionId: vi.fn(), + fetchSessionKeyByCustomerId: vi.fn(), + updateUser: vi.fn(), + }, })); -vi.mocked( - await import("rusty-motors-database"), -).databaseManager; - -describe("npsPortRouter", () => { - beforeEach(() => { - vi.resetAllMocks(); - }); - - it("should log an error and close the socket if local port is undefined", async () => { - const mockSocket = { - localPort: undefined, - end: vi.fn(), - on: vi.fn(), - }; - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket as unknown as Socket, - connectionId: "test-id", - connectedAt: Date.now(), - }; - - try { - await npsPortRouter({ taggedSocket }); - } catch (error) { - expect(error).toBeUndefined(); - } - - expect(mockSocket.end).toHaveBeenCalled(); - }); - - it("should log the start of the router and send ok to login packet for port 7003", async () => { - const mockSocket = { - localPort: 7003, - write: vi.fn(), - on: vi.fn(), - }; - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket as unknown as Socket, - connectionId: "test-id-nps", - connectedAt: Date.now(), - }; - - await npsPortRouter({ taggedSocket }).catch((error) => { - expect(error).toBeUndefined(); - }); - - - expect(mockSocket.write).toHaveBeenCalledWith( - Buffer.from([0x02, 0x30, 0x00, 0x00]), - ); - }); - - it("should handle data event and route initial message", async () => { - const mockSocket = { - localPort: 8228, - write: vi.fn(), - on: vi.fn((event, callback) => { - if (event === "data") { - try { - callback(Buffer.from([0x01, 0x02, 0x03])); - } catch (error) { - expect(error).toBeUndefined(); - } - } - }), - }; - - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket as unknown as Socket, - connectionId: "test-id-nps", - connectedAt: Date.now(), - }; - - const mockGamePacket = { - deserialize: vi.fn(), - toHexString: vi.fn().mockReturnValue("010203"), - }; - vi.spyOn(GamePacket.prototype, "deserialize").mockImplementation( - mockGamePacket.deserialize, - ); - vi.spyOn(GamePacket.prototype, "toHexString").mockImplementation( - mockGamePacket.toHexString, - ); - - try { - await npsPortRouter({ taggedSocket }); - } catch (error) { - expect(error).toBeUndefined(); - } - - }); - - - it("should log socket error event", async () => { - const mockSocket = { - localPort: 7003, - on: vi.fn((event, callback) => { - if (event === "error") { - callback(new Error("Test error")); - } - }), - write: vi.fn(), - } as unknown as Socket; - const mockLogger: ServerLogger = { - error: vi.fn(), - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - fatal: vi.fn(), - trace: vi.fn(), - child: vi.fn(), - }; - const taggedSocket: TaggedSocket = { - rawSocket: mockSocket, - connectionId: "test-id", - connectedAt: Date.now(), - }; - await npsPortRouter({ taggedSocket, log: mockLogger }).catch((error) => { - expect(error).toBeUndefined(); - }); - - expect(mockLogger.error).toHaveBeenCalledWith( - "[test-id] Socket error: Error: Test error", - ); - }); + + +describe('npsPortRouter', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it('should log an error and close the socket if local port is undefined', async () => { + const mockSocket = { + localPort: undefined, + end: vi.fn(), + on: vi.fn(), + }; + const taggedSocket: TaggedSocket = { + rawSocket: mockSocket as unknown as Socket, + connectionId: 'test-id', + connectedAt: Date.now(), + }; + + try { + await npsPortRouter({ taggedSocket }); + } catch (error) { + expect(error).toBeUndefined(); + } + + expect(mockSocket.end).toHaveBeenCalled(); + }); + + it('should log the start of the router and send ok to login packet for port 7003', async () => { + const mockSocket = { + localPort: 7003, + write: vi.fn(), + on: vi.fn(), + }; + const taggedSocket: TaggedSocket = { + rawSocket: mockSocket as unknown as Socket, + connectionId: 'test-id-nps', + connectedAt: Date.now(), + }; + + await npsPortRouter({ taggedSocket }).catch((error) => { + expect(error).toBeUndefined(); + }); + + expect(mockSocket.write).toHaveBeenCalledWith( + Buffer.from([0x02, 0x30, 0x00, 0x04]), + ); + }); + + it('should handle data event and route initial message', async () => { + const mockSocket = { + localPort: 8228, + write: vi.fn(), + on: vi.fn((event, callback) => { + if (event === 'data') { + try { + callback(Buffer.from([0x01, 0x02, 0x03])); + } catch (error) { + expect(error).toBeUndefined(); + } + } + }), + }; + + const taggedSocket: TaggedSocket = { + rawSocket: mockSocket as unknown as Socket, + connectionId: 'test-id-nps', + connectedAt: Date.now(), + }; + + const mockGamePacket = { + deserialize: vi.fn(), + toHexString: vi.fn().mockReturnValue('010203'), + }; + vi.spyOn(GamePacket.prototype, 'deserialize').mockImplementation( + mockGamePacket.deserialize, + ); + vi.spyOn(GamePacket.prototype, 'toHexString').mockImplementation( + mockGamePacket.toHexString, + ); + + try { + await npsPortRouter({ taggedSocket }); + } catch (error) { + expect(error).toBeUndefined(); + } + }); + + it('should log socket error event', async () => { + const mockSocket = { + localPort: 7003, + on: vi.fn((event, callback) => { + if (event === 'error') { + callback(new Error('Test error')); + } + }), + write: vi.fn(), + } as unknown as Socket; + const mockLogger: ServerLogger = { + error: vi.fn(), + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + fatal: vi.fn(), + trace: vi.fn(), + child: vi.fn(), + }; + const taggedSocket: TaggedSocket = { + rawSocket: mockSocket, + connectionId: 'test-id', + connectedAt: Date.now(), + }; + await npsPortRouter({ taggedSocket, log: mockLogger }).catch( + (error) => { + expect(error).toBeUndefined(); + }, + ); + + expect(mockLogger.error).toHaveBeenCalledWith( + '[test-id] Socket error: Error: Test error', + ); + }); }); diff --git a/packages/lobby/index.ts b/packages/lobby/index.ts index e3955ded6..84170b2ac 100644 --- a/packages/lobby/index.ts +++ b/packages/lobby/index.ts @@ -1 +1,17 @@ -export { receiveLobbyData } from "./src/internal.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export { receiveLobbyData } from './src/internal.js'; diff --git a/packages/lobby/package.json b/packages/lobby/package.json index 446393bcf..1933b49a7 100644 --- a/packages/lobby/package.json +++ b/packages/lobby/package.json @@ -21,6 +21,7 @@ "dependencies": { "@fastify/sensible": "^6.0.3", "fastify": "^5.3.3", + "rusty-motors-logger": "workspace:^", "rusty-motors-shared": "workspace:1.0.0-next.0" }, "directories": { diff --git a/packages/lobby/src/handlers/_setMyUserData.ts b/packages/lobby/src/handlers/_setMyUserData.ts index 14c55c498..410ef99f4 100644 --- a/packages/lobby/src/handlers/_setMyUserData.ts +++ b/packages/lobby/src/handlers/_setMyUserData.ts @@ -1,47 +1,50 @@ -import { LegacyMessage } from "rusty-motors-shared"; -import { UserInfo } from "../UserInfoMessage.js"; -import { databaseManager } from "rusty-motors-database"; -import { ServerLogger, getServerLogger } from "rusty-motors-shared"; - +import { LegacyMessage } from 'rusty-motors-shared'; +import { UserInfo } from '../UserInfoMessage.js'; +import { databaseService } from 'rusty-motors-database'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; export async function _setMyUserData({ - connectionId, - message, - log = getServerLogger("handlers/_setMyUserData"), + connectionId, + message, + log = getServerLogger('handlers/_setMyUserData'), }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }) { - try { - log.debug("Handling NPS_SET_MY_USER_DATA"); - log.debug(`Received command: ${message.serialize().toString("hex")}`); - - const incomingMessage = new UserInfo(); - incomingMessage.deserialize(message.serialize()); - - log.debug(`User ID: ${incomingMessage._userId}`); - - // Update the user's data - databaseManager.updateUser({ - userId: incomingMessage._userId, - userData: incomingMessage._userData, - }); - - // Build the packet - const packetResult = new LegacyMessage(); - packetResult._header.id = 516; - packetResult.deserialize(incomingMessage.serialize()); - - log.debug(`Sending response: ${packetResult.serialize().toString("hex")}`); - - return { - connectionId, - message: null, - }; - } catch (error) { - const err = Error(`Error handling NPS_SET_MY_USER_DATA: ${String(error)}`); - err.cause = error; - throw err; - } + try { + log.debug('Handling NPS_SET_MY_USER_DATA'); + log.debug(`Received command: ${message.serialize().toString('hex')}`); + + const incomingMessage = new UserInfo(); + incomingMessage.deserialize(message.serialize()); + + log.debug(`User ID: ${incomingMessage._userId}`); + + // Update the user's data + await databaseService.updateUser({ + userId: incomingMessage._userId, + userData: incomingMessage._userData, + }); + + // Build the packet + const packetResult = new LegacyMessage(); + packetResult._header.id = 516; + packetResult.deserialize(incomingMessage.serialize()); + + log.debug( + `Sending response: ${packetResult.serialize().toString('hex')}`, + ); + + return { + connectionId, + message: null, + }; + } catch (error) { + const err = Error( + `Error handling NPS_SET_MY_USER_DATA: ${String(error)}`, + ); + err.cause = error; + throw err; + } } diff --git a/packages/lobby/src/handlers/encryptedCommand.ts b/packages/lobby/src/handlers/encryptedCommand.ts index 9c2d560da..e85541c8c 100644 --- a/packages/lobby/src/handlers/encryptedCommand.ts +++ b/packages/lobby/src/handlers/encryptedCommand.ts @@ -1,16 +1,15 @@ import { - fetchStateFromDatabase, - getEncryption, - ServerLogger, - updateEncryption, -} from "rusty-motors-shared"; -import { MessageBufferOld } from "rusty-motors-shared"; -import { SerializedBufferOld } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { _setMyUserData } from "./_setMyUserData.js"; -import { handleGetMiniUserList } from "./handleGetMiniUserList.js"; -import { handleSendMiniRiffList } from "./handleSendMiniRiffList.js"; -import { getServerLogger } from "rusty-motors-shared"; + fetchStateFromDatabase, + getEncryption, + updateEncryption, +} from 'rusty-motors-shared'; +import { MessageBufferOld } from 'rusty-motors-shared'; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { _setMyUserData } from './_setMyUserData.js'; +import { handleGetMiniUserList } from './handleGetMiniUserList.js'; +import { handleSendMiniRiffList } from './handleSendMiniRiffList.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** * Array of supported command handlers @@ -28,16 +27,16 @@ import { getServerLogger } from "rusty-motors-shared"; * }>}[]} */ export const messageHandlers: { - opCode: number; - name: string; - handler: (args: { - connectionId: string; - message: SerializedBufferOld; - log: ServerLogger; - }) => Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; - }>; + opCode: number; + name: string; + handler: (args: { + connectionId: string; + message: SerializedBufferOld; + log: ServerLogger; + }) => Promise<{ + connectionId: string; + messages: SerializedBufferOld[]; + }>; }[] = []; /** @@ -53,39 +52,39 @@ export const messageHandlers: { * }>} */ async function encryptCmd({ - connectionId, - message, - log = getServerLogger( "lobby.encryptCmd"), + connectionId, + message, + log = getServerLogger('lobby.encryptCmd'), }: { - connectionId: string; - message: LegacyMessage | MessageBufferOld; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage | MessageBufferOld; + log?: ServerLogger; }): Promise<{ - connectionId: string; - message: LegacyMessage | MessageBufferOld; + connectionId: string; + message: LegacyMessage | MessageBufferOld; }> { - const state = fetchStateFromDatabase(); + const state = fetchStateFromDatabase(); - const encryption = getEncryption(state, connectionId); + const encryption = getEncryption(state, connectionId); - if (typeof encryption === "undefined") { - throw Error( - `Unable to locate encryption session for connection id ${connectionId}`, - ); - } + if (typeof encryption === 'undefined') { + throw Error( + `Unable to locate encryption session for connection id ${connectionId}`, + ); + } - const result = encryption.commandEncryption.encrypt(message.data); + const result = encryption.commandEncryption.encrypt(message.data); - updateEncryption(state, encryption).save(); + updateEncryption(state, encryption).save(); - log.debug(`[ciphered Cmd: ${result.toString("hex")}`); + log.debug(`[ciphered Cmd: ${result.toString('hex')}`); - message.setBuffer(result); + message.setBuffer(result); - return { - connectionId, - message, - }; + return { + connectionId, + message, + }; } /** @@ -101,70 +100,70 @@ async function encryptCmd({ * }>} */ async function decryptCmd({ - connectionId, - message, - log = getServerLogger( "lobby.decryptCmd"), + connectionId, + message, + log = getServerLogger('lobby.decryptCmd'), }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - message: LegacyMessage; + connectionId: string; + message: LegacyMessage; }> { - const state = fetchStateFromDatabase(); + const state = fetchStateFromDatabase(); - const encryption = getEncryption(state, connectionId); + const encryption = getEncryption(state, connectionId); - if (typeof encryption === "undefined") { - throw Error( - `Unable to locate encryption session for connection id ${connectionId}`, - ); - } + if (typeof encryption === 'undefined') { + throw Error( + `Unable to locate encryption session for connection id ${connectionId}`, + ); + } - const result = encryption.commandEncryption.decrypt(message.data); + const result = encryption.commandEncryption.decrypt(message.data); - updateEncryption(state, encryption).save(); + updateEncryption(state, encryption).save(); - log.debug(`[Deciphered Cmd: ${result.toString("hex")}`); + log.debug(`[Deciphered Cmd: ${result.toString('hex')}`); - message.setBuffer(result); + message.setBuffer(result); - return { - connectionId, - message, - }; + return { + connectionId, + message, + }; } export type NpsCommandHandler = { - opCode: number; - name: string; - handler: (args: { - connectionId: string; - message: LegacyMessage; - log: ServerLogger; - }) => Promise<{ - connectionId: string; - message: LegacyMessage | null; - }>; + opCode: number; + name: string; + handler: (args: { + connectionId: string; + message: LegacyMessage; + log: ServerLogger; + }) => Promise<{ + connectionId: string; + message: LegacyMessage | null; + }>; }; const npsCommandHandlers: NpsCommandHandler[] = [ - { - opCode: 0x128, - name: "NPS_GET_MINI_USER_LIST", - handler: handleGetMiniUserList, - }, - { - opCode: 0x30c, - name: "NPS_SEND_MINI_RIFF_LIST", - handler: handleSendMiniRiffList, - }, - { - opCode: 0x103, - name: "NPS_SET_MY_USER_DATA", - handler: _setMyUserData, - }, + { + opCode: 0x128, + name: 'NPS_GET_MINI_USER_LIST', + handler: handleGetMiniUserList, + }, + { + opCode: 0x30c, + name: 'NPS_SEND_MINI_RIFF_LIST', + handler: handleSendMiniRiffList, + }, + { + opCode: 0x103, + name: 'NPS_SET_MY_USER_DATA', + handler: _setMyUserData, + }, ]; /** @@ -180,39 +179,39 @@ const npsCommandHandlers: NpsCommandHandler[] = [ * }>}} */ async function handleCommand({ - connectionId, - message, - log = getServerLogger( "lobby.handleCommand"), + connectionId, + message, + log = getServerLogger('lobby.handleCommand'), }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - message: MessageBufferOld | LegacyMessage | null; + connectionId: string; + message: MessageBufferOld | LegacyMessage | null; }> { - const incommingRequest = message; + const incommingRequest = message; - log.debug( - `[${connectionId}] Received command: ${incommingRequest._doSerialize().toString("hex")}`, - ); + log.debug( + `[${connectionId}] Received command: ${incommingRequest._doSerialize().toString('hex')}`, + ); - // What is the command? - const command = incommingRequest.data.readUInt16BE(0); + // What is the command? + const command = incommingRequest.data.readUInt16BE(0); - log.debug(`Command: ${command}`); + log.debug(`Command: ${command}`); - const handler = npsCommandHandlers.find((h) => h.opCode === command); + const handler = npsCommandHandlers.find((h) => h.opCode === command); - if (typeof handler === "undefined") { - throw Error(`Unknown command: ${command}`); - } + if (typeof handler === 'undefined') { + throw Error(`Unknown command: ${command}`); + } - return handler.handler({ - connectionId, - message, - log, - }); + return handler.handler({ + connectionId, + message, + log, + }); } /** @@ -229,53 +228,53 @@ async function handleCommand({ */ export async function handleEncryptedNPSCommand({ - connectionId, - message, - log = getServerLogger( "lobby.handleEncryptedNPSCommand"), + connectionId, + message, + log = getServerLogger('lobby.handleEncryptedNPSCommand'), }: { - connectionId: string; - message: SerializedBufferOld; - log?: ServerLogger; + connectionId: string; + message: SerializedBufferOld; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - const inboundMessage = new LegacyMessage(); - inboundMessage._doDeserialize(message.data); - - // Decipher - const decipheredMessage = await decryptCmd({ - connectionId, - message: inboundMessage, - log, - }); - - const response = await handleCommand({ - connectionId, - message: decipheredMessage.message, - log, - }); - - if (response.message === null) { - log.debug(`[${connectionId}] No response to send`); - return { - connectionId, - messages: [], - }; - } - - // Encipher - const encryptedResponse = encryptCmd({ - connectionId, - message: response.message, - log, - }); - - const outboundMessage = new SerializedBufferOld(); - outboundMessage.setBuffer((await encryptedResponse).message.serialize()); - - return { - connectionId, - messages: [outboundMessage], - }; + const inboundMessage = new LegacyMessage(); + inboundMessage._doDeserialize(message.data); + + // Decipher + const decipheredMessage = await decryptCmd({ + connectionId, + message: inboundMessage, + log, + }); + + const response = await handleCommand({ + connectionId, + message: decipheredMessage.message, + log, + }); + + if (response.message === null) { + log.debug(`[${connectionId}] No response to send`); + return { + connectionId, + messages: [], + }; + } + + // Encipher + const encryptedResponse = encryptCmd({ + connectionId, + message: response.message, + log, + }); + + const outboundMessage = new SerializedBufferOld(); + outboundMessage.setBuffer((await encryptedResponse).message.serialize()); + + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/lobby/src/handlers/handleGetMiniUserList.ts b/packages/lobby/src/handlers/handleGetMiniUserList.ts index 1d7d1febf..fe6a4785b 100644 --- a/packages/lobby/src/handlers/handleGetMiniUserList.ts +++ b/packages/lobby/src/handlers/handleGetMiniUserList.ts @@ -1,16 +1,15 @@ -import { GameMessage, ServerLogger } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { serializeString } from "rusty-motors-shared"; -import { UserInfo } from "../UserInfoMessage.js"; -import { channelRecordSize, channels } from "./channels.js"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("Lobby"); +import { GameMessage } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { serializeString } from 'rusty-motors-shared'; +import { UserInfo } from '../UserInfoMessage.js'; +import { channelRecordSize, channels } from './channels.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; +const defaultLogger = getServerLogger('Lobby'); const user1 = new UserInfo(); user1._userId = 1; -user1._userName = "User 1"; +user1._userName = 'User 1'; /** * @param {object} args * @param {string} args.connectionId @@ -19,58 +18,62 @@ user1._userName = "User 1"; */ export async function handleGetMiniUserList({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }) { - log.debug("Handling NPS_GET_MINI_USER_LIST"); - log.debug(`Received command: ${message._doSerialize().toString("hex")}`); + log.debug('Handling NPS_GET_MINI_USER_LIST'); + log.debug(`Received command: ${message._doSerialize().toString('hex')}`); - const outgoingGameMessage = new GameMessage(553); + const outgoingGameMessage = new GameMessage(553); - const resultSize = channelRecordSize * channels.length - 12; + const resultSize = channelRecordSize * channels.length - 12; - const packetContent = Buffer.alloc(resultSize); + const packetContent = Buffer.alloc(resultSize); - let offset = 0; - try { - // Add the response code - packetContent.writeUInt32BE(17, offset); - offset += 4; // offset is 8 + let offset = 0; + try { + // Add the response code + packetContent.writeUInt32BE(17, offset); + offset += 4; // offset is 8 - packetContent.writeUInt32BE(1, offset); - offset += 4; // offset is 12 + packetContent.writeUInt32BE(1, offset); + offset += 4; // offset is 12 - // Write the count of users - packetContent.writeUInt32BE(1, offset); - offset += 4; // offset is 16 + // Write the count of users + packetContent.writeUInt32BE(1, offset); + offset += 4; // offset is 16 - // write the persona id - packetContent.writeUInt32BE(user1._userId, offset); - offset += 4; // offset is 20 + // write the persona id + packetContent.writeUInt32BE(user1._userId, offset); + offset += 4; // offset is 20 - // write the persona name - serializeString(user1._userName, packetContent, offset); + // write the persona name + serializeString(user1._userName, packetContent, offset); - outgoingGameMessage.setRecordData(packetContent); + outgoingGameMessage.setRecordData(packetContent); - // Build the packet - const packetResult = new LegacyMessage(); - packetResult._doDeserialize(outgoingGameMessage.serialize()); + // Build the packet + const packetResult = new LegacyMessage(); + packetResult._doDeserialize(outgoingGameMessage.serialize()); - log.debug(`Sending response: ${packetResult.serialize().toString("hex")}`); + log.debug( + `Sending response: ${packetResult.serialize().toString('hex')}`, + ); - return { - connectionId, - message: packetResult, - }; - } catch (error) { - const err = Error(`Error handling NPS_MINI_USER_LIST: ${String(error)}`); - err.cause = error; - throw err; - } + return { + connectionId, + message: packetResult, + }; + } catch (error) { + const err = Error( + `Error handling NPS_MINI_USER_LIST: ${String(error)}`, + ); + err.cause = error; + throw err; + } } diff --git a/packages/lobby/src/handlers/handleSendMiniRiffList.ts b/packages/lobby/src/handlers/handleSendMiniRiffList.ts index bd07ca264..0c1674d20 100644 --- a/packages/lobby/src/handlers/handleSendMiniRiffList.ts +++ b/packages/lobby/src/handlers/handleSendMiniRiffList.ts @@ -1,11 +1,10 @@ -import { ServerLogger, } from "rusty-motors-shared"; -import { GameMessage } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { serializeString } from "rusty-motors-shared"; -import { channelRecordSize, channels } from "./channels.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { GameMessage } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { serializeString } from 'rusty-motors-shared'; +import { channelRecordSize, channels } from './channels.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("Lobby"); +const defaultLogger = getServerLogger('Lobby'); // const users = [user1]; /** @@ -15,55 +14,57 @@ const defaultLogger = getServerLogger("Lobby"); * @param {ServerLogger} [args.log=getServerLogger({ name: "Lobby" })] */ export async function handleSendMiniRiffList({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }) { - log.debug("Handling NPS_SEND_MINI_RIFF_LIST"); - log.debug(`Received command: ${message._doSerialize().toString("hex")}`); + log.debug('Handling NPS_SEND_MINI_RIFF_LIST'); + log.debug(`Received command: ${message._doSerialize().toString('hex')}`); - const outgoingGameMessage = new GameMessage(1028); + const outgoingGameMessage = new GameMessage(1028); - const resultSize = channelRecordSize * channels.length - 12; + const resultSize = channelRecordSize * channels.length - 12; - const packetContent = Buffer.alloc(resultSize); + const packetContent = Buffer.alloc(resultSize); - let offset = 0; - try { - packetContent.writeUInt32BE(channels.length, offset); - offset += 4; // offset is 8 + let offset = 0; + try { + packetContent.writeUInt32BE(channels.length, offset); + offset += 4; // offset is 8 - // loop through the channels - for (const channel of channels) { - offset = serializeString(channel.name, packetContent, offset); + // loop through the channels + for (const channel of channels) { + offset = serializeString(channel.name, packetContent, offset); - packetContent.writeUInt32BE(channel.id, offset); - offset += 4; - packetContent.writeUInt16BE(channel.population, offset); - offset += 2; - } + packetContent.writeUInt32BE(channel.id, offset); + offset += 4; + packetContent.writeUInt16BE(channel.population, offset); + offset += 2; + } - outgoingGameMessage.setRecordData(packetContent); + outgoingGameMessage.setRecordData(packetContent); - // Build the packet - const packetResult = new LegacyMessage(); - packetResult._doDeserialize(outgoingGameMessage.serialize()); + // Build the packet + const packetResult = new LegacyMessage(); + packetResult._doDeserialize(outgoingGameMessage.serialize()); - log.debug(`Sending response: ${packetResult.serialize().toString("hex")}`); + log.debug( + `Sending response: ${packetResult.serialize().toString('hex')}`, + ); - return { - connectionId, - message: packetResult, - }; - } catch (error) { - const err = Error( - `Error handling NPS_SEND_MINI_RIFF_LIST: ${String(error)}`, - ); - err.cause = error; - throw err; - } + return { + connectionId, + message: packetResult, + }; + } catch (error) { + const err = Error( + `Error handling NPS_SEND_MINI_RIFF_LIST: ${String(error)}`, + ); + err.cause = error; + throw err; + } } diff --git a/packages/lobby/src/handlers/handleTrackingPing.ts b/packages/lobby/src/handlers/handleTrackingPing.ts index 586309390..3e13bdb27 100644 --- a/packages/lobby/src/handlers/handleTrackingPing.ts +++ b/packages/lobby/src/handlers/handleTrackingPing.ts @@ -1,27 +1,27 @@ -import { SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { getServerLogger } from "rusty-motors-shared"; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("Lobby"); +const defaultLogger = getServerLogger('Lobby'); export async function handleTrackingPing({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: SerializedBufferOld; - log?: ServerLogger; + connectionId: string; + message: SerializedBufferOld; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - log.debug("Handling NPS_TRACKING_PING"); - log.debug(`Received command: ${message.toString()}`); + log.debug('Handling NPS_TRACKING_PING'); + log.debug(`Received command: ${message.toString()}`); - log.debug("Skipping response"); + log.debug('Skipping response'); - return { - connectionId, - messages: [], - }; + return { + connectionId, + messages: [], + }; } diff --git a/packages/lobby/src/handlers/heartbeat.js b/packages/lobby/src/handlers/heartbeat.js index 6bbc84081..d3bb7ac8c 100644 --- a/packages/lobby/src/handlers/heartbeat.js +++ b/packages/lobby/src/handlers/heartbeat.js @@ -1,27 +1,27 @@ -import { getServerLogger } from "rusty-motors-shared"; -import { NPSMessage, SerializedBuffer } from "rusty-motors-shared"; +import { getServerLogger } from 'rusty-motors-logger'; +import { NPSMessage, SerializedBuffer } from 'rusty-motors-shared'; export async function _npsHeartbeat({ - connectionId, - // biome-ignore lint/correctness/noUnusedVariables: - message, - log = getServerLogger({ - name: "_npsHeartbeat", - }), + connectionId, + // biome-ignore lint/correctness/noUnusedVariables: + message, + log = getServerLogger({ + name: '_npsHeartbeat', + }), }) { - const packetContent = Buffer.alloc(8); - const packetResult = new NPSMessage(); - packetResult._header.id = 0x127; - packetResult.setBuffer(packetContent); + const packetContent = Buffer.alloc(8); + const packetResult = new NPSMessage(); + packetResult._header.id = 0x127; + packetResult.setBuffer(packetContent); - log.debug("Dumping packet..."); - log.debug(packetResult.toString()); + log.debug('Dumping packet...'); + log.debug(packetResult.toString()); - const outboundMessage = new SerializedBuffer(); - outboundMessage.deserialize(packetResult.serialize()); + const outboundMessage = new SerializedBuffer(); + outboundMessage.deserialize(packetResult.serialize()); - return { - connectionId, - messages: [outboundMessage], - }; + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/lobby/src/handlers/requestConnectGameServer.ts b/packages/lobby/src/handlers/requestConnectGameServer.ts index e978ca59e..8808a5c6a 100644 --- a/packages/lobby/src/handlers/requestConnectGameServer.ts +++ b/packages/lobby/src/handlers/requestConnectGameServer.ts @@ -1,22 +1,21 @@ -import { getPersonasByPersonaId } from "rusty-motors-personas"; -import { type ServiceArgs } from "rusty-motors-shared"; -import { LoginInfoMessage } from "../LoginInfoMessage.js"; +import { getPersonasByPersonaId } from 'rusty-motors-personas'; +import { type ServiceArgs } from 'rusty-motors-shared'; +import { LoginInfoMessage } from '../LoginInfoMessage.js'; import { - createCommandEncryptionPair, - createDataEncryptionPair, -} from "rusty-motors-gateway"; + createCommandEncryptionPair, + createDataEncryptionPair, +} from 'rusty-motors-gateway'; import { - McosEncryption, - addEncryption, - fetchStateFromDatabase, - getEncryption, -} from "rusty-motors-shared"; -import { SerializedBufferOld } from "rusty-motors-shared"; -import { UserInfoMessage } from "../UserInfoMessage.js"; -import { databaseManager } from "rusty-motors-database"; -import { getServerLogger } from "rusty-motors-shared"; - + McosEncryption, + addEncryption, + fetchStateFromDatabase, + getEncryption, +} from 'rusty-motors-shared'; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { UserInfoMessage } from '../UserInfoMessage.js'; +import { databaseService } from 'rusty-motors-database'; +import { getServerLogger } from 'rusty-motors-logger'; /** * Convert to zero padded hex @@ -26,12 +25,12 @@ import { getServerLogger } from "rusty-motors-shared"; * @return {string} */ export function toHex(data: Buffer): string { - /** @type {string[]} */ - const bytes: string[] = []; - data.forEach((b: number) => { - bytes.push(b.toString(16).toUpperCase().padStart(2, "0")); - }); - return bytes.join(""); + /** @type {string[]} */ + const bytes: string[] = []; + data.forEach((b: number) => { + bytes.push(b.toString(16).toUpperCase().padStart(2, '0')); + }); + return bytes.join(''); } /** @@ -45,85 +44,88 @@ export function toHex(data: Buffer): string { * }>} */ export async function _npsRequestGameConnectServer({ - connectionId, - message, - log = getServerLogger("handlers/_npsRequestGameConnectServer"), + connectionId, + message, + log = getServerLogger('handlers/_npsRequestGameConnectServer'), }: ServiceArgs): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - // This is a NPS_LoginInfo packet - // As a legacy packet, it used the old NPSMessage format - // of a 4 byte header, followed by a 4 byte length, followed - // by the data payload. - - const inboundMessage = new LoginInfoMessage(); - inboundMessage.deserialize(message.data); - - log.debug(`LoginInfoMessage: ${inboundMessage.toString()}`); - - const personas = await getPersonasByPersonaId({ - personaId: inboundMessage._userId, - }); - if (typeof personas[0] === "undefined") { - const err = Error("No personas found."); - throw err; - } - - const { customerId } = personas[0]; - - const state = fetchStateFromDatabase(); - - const existingEncryption = getEncryption(state, connectionId); - - if (!existingEncryption) { - // Set the encryption keys on the lobby connection - const keys = await databaseManager.fetchSessionKeyByCustomerId(customerId); - - if (keys === undefined) { - throw Error("Error fetching session keys!"); - } - - // We have the session keys, set them on the connection - try { - const newCommandEncryptionPair = createCommandEncryptionPair( - keys.sessionKey, - ); - - const newDataEncryptionPair = createDataEncryptionPair(keys.sessionKey); - - const newEncryption = new McosEncryption({ - connectionId, - commandEncryptionPair: newCommandEncryptionPair, - dataEncryptionPair: newDataEncryptionPair, - }); - - addEncryption(state, newEncryption).save(); - } catch (error) { - const err = Error(`Error creating encryption`); - err.cause = error; - throw err; - } - } - - // We have a session, we are good to go! - // Send the response packet - - const responsePacket = new UserInfoMessage(); - responsePacket.fromLoginInfoMessage(inboundMessage); - - responsePacket._header.id = 0x120; - - // log the packet - log.debug( - `!!! outbound lobby login response packet: ${responsePacket.toString()}`, - ); - - const outboundMessage = new SerializedBufferOld(); - outboundMessage._doDeserialize(responsePacket.serialize()); - - return { - connectionId, - messages: [outboundMessage], - }; + // This is a NPS_LoginInfo packet + // As a legacy packet, it used the old NPSMessage format + // of a 4 byte header, followed by a 4 byte length, followed + // by the data payload. + + const inboundMessage = new LoginInfoMessage(); + inboundMessage.deserialize(message.data); + + log.debug(`LoginInfoMessage: ${inboundMessage.toString()}`); + + const personas = await getPersonasByPersonaId({ + personaId: inboundMessage._userId, + }); + if (typeof personas[0] === 'undefined') { + const err = Error('No personas found.'); + throw err; + } + + const { customerId } = personas[0]; + + const state = fetchStateFromDatabase(); + + const existingEncryption = getEncryption(state, connectionId); + + if (!existingEncryption) { + // Set the encryption keys on the lobby connection + const keys = + await databaseService.fetchSessionKeyByCustomerId(customerId); + + if (keys === undefined) { + throw Error('Error fetching session keys!'); + } + + // We have the session keys, set them on the connection + try { + const newCommandEncryptionPair = createCommandEncryptionPair( + keys.sessionKey, + ); + + const newDataEncryptionPair = createDataEncryptionPair( + keys.sessionKey, + ); + + const newEncryption = new McosEncryption({ + connectionId, + commandEncryptionPair: newCommandEncryptionPair, + dataEncryptionPair: newDataEncryptionPair, + }); + + addEncryption(state, newEncryption).save(); + } catch (error) { + const err = Error("Error creating encryption"); + err.cause = error; + throw err; + } + } + + // We have a session, we are good to go! + // Send the response packet + + const responsePacket = new UserInfoMessage(); + responsePacket.fromLoginInfoMessage(inboundMessage); + + responsePacket._header.id = 0x120; + + // log the packet + log.debug( + `!!! outbound lobby login response packet: ${responsePacket.toString()}`, + ); + + const outboundMessage = new SerializedBufferOld(); + outboundMessage._doDeserialize(responsePacket.serialize()); + + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/lobby/src/internal.ts b/packages/lobby/src/internal.ts index 7d5dae7c7..6a9604c3a 100644 --- a/packages/lobby/src/internal.ts +++ b/packages/lobby/src/internal.ts @@ -14,14 +14,14 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { NPSMessage } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { handleEncryptedNPSCommand } from "./handlers/encryptedCommand.js"; -import { handleTrackingPing } from "./handlers/handleTrackingPing.js"; -import { _npsRequestGameConnectServer } from "./handlers/requestConnectGameServer.js"; -import type { BufferSerializer } from "rusty-motors-shared-packets"; -import { getServerLogger } from "rusty-motors-shared"; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { NPSMessage } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { handleEncryptedNPSCommand } from './handlers/encryptedCommand.js'; +import { handleTrackingPing } from './handlers/handleTrackingPing.js'; +import { _npsRequestGameConnectServer } from './handlers/requestConnectGameServer.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; +import { BytableMessage } from '@rustymotors/binary'; /** * Array of supported message handlers @@ -39,106 +39,109 @@ import { getServerLogger } from "rusty-motors-shared"; * }>}[]} */ export const messageHandlers: { - opCode: number; - name: string; - handler: (args: { - connectionId: string; - message: SerializedBufferOld; - log?: ServerLogger; - }) => Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; - }>; + opCode: number; + name: string; + handler: (args: { + connectionId: string; + message: SerializedBufferOld; + log?: ServerLogger; + }) => Promise<{ + connectionId: string; + messages: SerializedBufferOld[]; + }>; }[] = [ - { - opCode: 256, // 0x100 - name: "User login", - handler: _npsRequestGameConnectServer, - }, - { - opCode: 4353, // 0x1101 - name: "Encrypted command", - handler: handleEncryptedNPSCommand, - }, - { - opCode: 535, // 0x0217 - name: "Tracking ping", - handler: handleTrackingPing, - }, + { + opCode: 256, // 0x100 + name: 'User login', + handler: _npsRequestGameConnectServer, + }, + { + opCode: 4353, // 0x1101 + name: 'Encrypted command', + handler: handleEncryptedNPSCommand, + }, + { + opCode: 535, // 0x0217 + name: 'Tracking ping', + handler: handleTrackingPing, + }, ]; /** - * @param {object} args - * @param {string} args.connectionId - * @param {SerializedBufferOld} args.message - * @param {ServerLogger} [args.log=getServerLogger({ name: "PersonaServer" })] - * @returns {Promise<{ - * connectionId: string, - * messages: SerializedBufferOld[], - * }>} - * @throws {Error} Unknown code was received + * Processes an incoming lobby data message, dispatching it to the appropriate handler based on its operation code. + * + * Determines the message type (legacy or NPS), deserializes the data, and invokes the corresponding handler. Returns the handler's response, which includes the connection ID and any resulting messages. + * + * @param connectionId - Identifier for the connection associated with the incoming message. + * @param message - The incoming lobby message to process. + * @param log - Optional logger instance; defaults to a logger named 'lobby.receiveLobbyData'. + * @returns An object containing the connection ID and an array of response messages. + * + * @throws {Error} If the message is too short to deserialize or if the operation code is unsupported. */ export async function receiveLobbyData({ - connectionId, - message, - log = getServerLogger( "lobby.receiveLobbyData" ), + connectionId, + message, + log = getServerLogger('lobby.receiveLobbyData'), }: { - connectionId: string; - message: BufferSerializer; - log?: ServerLogger; + connectionId: string; + message: BytableMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - /** @type {LegacyMessage | NPSMessage} */ - let inboundMessage: LegacyMessage | NPSMessage; + /** @type {LegacyMessage | NPSMessage} */ + let inboundMessage: LegacyMessage | NPSMessage; - // Check data length - const dataLength = message.getByteSize(); + // Check data length + const dataLength = message.getByteSize(); - if (dataLength < 4) { - throw Error(`Data length ${dataLength} is too short to deserialize`); - } + if (dataLength < 4) { + throw Error(`Data length ${dataLength} is too short to deserialize`); + } - if (dataLength > 12) { - inboundMessage = new NPSMessage(); - } else { - inboundMessage = new LegacyMessage(); - } + if (dataLength > 12) { + inboundMessage = new NPSMessage(); + } else { + inboundMessage = new LegacyMessage(); + } - inboundMessage._doDeserialize(message.serialize()); + inboundMessage.deserialize(message.serialize()); - const data = message.serialize(); - log.debug( - `Received Lobby packet', + const data = message.serialize(); + log.debug( + `Received Lobby packet', ${JSON.stringify({ - data: data.toString("hex"), - })}`, - ); + data: data.toString('hex'), + })}`, + ); - const supportedHandler = messageHandlers.find((h) => { - return h.opCode === inboundMessage._header.id; - }); + const supportedHandler = messageHandlers.find((h) => { + return h.opCode === inboundMessage._header.messageId; + }); - if (typeof supportedHandler === "undefined") { - // We do not yet support this message code - throw Error(`UNSUPPORTED_MESSAGECODE: ${inboundMessage._header.id}`); - } + if (typeof supportedHandler === 'undefined') { + // We do not yet support this message code + throw Error( + `UNSUPPORTED_MESSAGECODE: ${inboundMessage._header.messageId}`, + ); + } - const buff = new SerializedBufferOld(); - buff._doDeserialize(data); + const buff = new SerializedBufferOld(); + buff._doDeserialize(data); - try { - const result = await supportedHandler.handler({ - connectionId, - message: buff, - }); - log.debug(`Returning with ${result.messages.length} messages`); - log.debug("Leaving receiveLobbyData"); - return result; - } catch (error) { - const err = Error(`Error handling lobby data: ${String(error)}`); - err.cause = error; - throw err; - } + try { + const result = await supportedHandler.handler({ + connectionId, + message: buff, + }); + log.debug(`Returning with ${result.messages.length} messages`); + log.debug('Leaving receiveLobbyData'); + return result; + } catch (error) { + const err = Error(`Error handling lobby data: ${String(error)}`); + err.cause = error; + throw err; + } } diff --git a/packages/logger/CHANGELOG.md b/packages/logger/CHANGELOG.md new file mode 100644 index 000000000..6acdaf6ea --- /dev/null +++ b/packages/logger/CHANGELOG.md @@ -0,0 +1,7 @@ +# rusty-motors-logger + +## 1.0.0-next.0 + +### Patch Changes + +- ae13a4c: Initial changeset diff --git a/packages/logger/index.ts b/packages/logger/index.ts new file mode 100644 index 000000000..e686d45c4 --- /dev/null +++ b/packages/logger/index.ts @@ -0,0 +1,2 @@ +export { getServerLogger } from './src/getServerLogger.js'; +export * from './src/interfaces.js'; diff --git a/packages/logger/package.json b/packages/logger/package.json new file mode 100644 index 000000000..b7ed67c09 --- /dev/null +++ b/packages/logger/package.json @@ -0,0 +1,39 @@ +{ + "name": "rusty-motors-logger", + "version": "1.0.0-next.0", + "exports": { + ".": { + "import": "./index.js", + "require": "./index.js" + }, + "./test": { + "import": "./test/index.js", + "require": "./test/index.js" + } + }, + "type": "module", + "scripts": { + "check": "tsc", + "lint": "npx @biomejs/biome lint --write .", + "format": "npx @biomejs/biome format --write .", + "test": "vitest run --coverage", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "AGPL-3.0", + "dependencies": { + "fastify": "^5.3.2", + "pino": "^9.6.0", + "pino-pretty": "^13.0.0", + "rusty-motors-shared-packets": "workspace:1.0.0-next.0" + }, + "directories": { + "test": "test" + }, + "description": "", + "devDependencies": { + "@vitest/coverage-v8": "3.1.3", + "vitest": "^3.1.3" + } +} \ No newline at end of file diff --git a/packages/logger/src/getServerLogger.ts b/packages/logger/src/getServerLogger.ts new file mode 100644 index 000000000..87a237ca1 --- /dev/null +++ b/packages/logger/src/getServerLogger.ts @@ -0,0 +1,74 @@ +import * as Sentry from '@sentry/node'; +import pino from 'pino'; +import { Logger } from './interfaces.js'; + +export function getServerLogger(name?: string): Logger { + if (logger) { + return logger.child({ name }); + } + const loggerName = name || 'core'; + const validLogLevels = [ + 'fatal', + 'error', + 'warn', + 'info', + 'debug', + 'trace', + ] as const; + const logLevel = process.env['MCO_LOG_LEVEL'] || 'debug'; + + if (!validLogLevels.includes(logLevel as LogLevel)) { + console.warn(`Invalid log level: ${logLevel}. Defaulting to "debug"`); + } + + logger = pino.default({ + name: loggerName, + transport: { + targets: [ + { + target: 'pino-pretty', + options: { + colorize: true, + translateTime: 'SYS:standard', + }, + level: logLevel, + }, + { + target: 'pino/file', + options: { + destination: './logs/server.log', + mkdir: true, + append: false, + }, + level: logLevel, + }, + ], + }, + level: logLevel, + }); + + return { + info: logger.info.bind(logger), + warn: logger.warn.bind(logger), + error: (msg: string, obj?: unknown) => { + if (obj instanceof Error) { + Sentry.captureException(obj); + } else if (obj) { + Sentry.captureException(new Error(msg), { + extra: { context: obj }, + }); + } else { + Sentry.captureException(new Error(msg)); + } + logger.error({ msg, obj }); + }, + fatal: logger.fatal.bind(logger), + debug: logger.debug.bind(logger), + trace: logger.trace.bind(logger), + child: (obj: pino.Bindings) => logger.child(obj), + }; +} + +export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; + +export let logger: pino.Logger; diff --git a/packages/logger/src/interfaces.ts b/packages/logger/src/interfaces.ts new file mode 100644 index 000000000..479a0f618 --- /dev/null +++ b/packages/logger/src/interfaces.ts @@ -0,0 +1,13 @@ +import pino from 'pino'; + +export interface Logger { + info: (msg: string, obj?: unknown) => void; + warn: (msg: string, obj?: unknown) => void; + error: (msg: string, obj?: unknown) => void; + fatal: (msg: string, obj?: unknown) => void; + debug: (msg: string, obj?: unknown) => void; + trace: (msg: string, obj?: unknown) => void; + child: (obj: pino.Bindings) => Logger; +} + +export type ServerLogger = Logger; diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json new file mode 100644 index 000000000..ba43189cf --- /dev/null +++ b/packages/logger/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "incremental": true, + "composite": true + }, + "include": ["index.ts", "src"] +} diff --git a/packages/login/src/NPSUserStatus.ts b/packages/login/src/NPSUserStatus.ts index d1145eb6f..991442eac 100644 --- a/packages/login/src/NPSUserStatus.ts +++ b/packages/login/src/NPSUserStatus.ts @@ -1,18 +1,18 @@ -import { privateDecrypt } from "node:crypto"; -import { readFileSync } from "node:fs"; +import { privateDecrypt } from 'node:crypto'; +import { readFileSync } from 'node:fs'; -import { Configuration, getServerLogger, ServerLogger, } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; +import { Configuration } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; const SESSION_KEY_START = 52; type NPSSessionKey = { - sessionKeyLength: number; - sessionKey: string; - expires: number; + sessionKeyLength: number; + sessionKey: string; + expires: number; }; - /** * * @@ -35,118 +35,120 @@ type NPSSessionKey = { * @property {Buffer} buffer */ export class NPSUserStatus extends LegacyMessage { - _config: Configuration; - log: ServerLogger; - sessionKey: string; - opCode: number; - contextId: string; - buffer: Buffer; - /** - * - * @param {Buffer} packet - * @param {Configuration} config - * @param {Serverogger} log - */ - constructor(packet: Buffer, config: Configuration, log: ServerLogger) { - super(); - this._config = config; - this.log = getServerLogger("NPSUserStatus"); - log.debug("Constructing NPSUserStatus"); - this._header._doDeserialize(packet); - this.sessionKey = ""; - - // Save the NPS opCode - this.opCode = packet.readInt16BE(0); - - // Save the contextId - this.contextId = packet.subarray(14, 48).toString(); - - // Save the raw packet - this.buffer = packet; - } - - - - - /** - * Extracts the session key from the provided raw packet buffer. - * - * This method reads the session key length from the packet, extracts the session key, - * decrypts it using the private key, and parses the decrypted session key. - * - * @param rawPacket - The raw packet buffer containing the session key. - * - * @throws {Error} Throws an error if there is an issue decrypting the session key. - */ - extractSessionKeyFromPacket(rawPacket: Buffer): void { - this.log.debug("Extracting key"); - - const keyLength = rawPacket.readInt16LE(SESSION_KEY_START); - - // Extract the session key which is 128 acsii characters (256 bytes) - const sessionKeyAsAscii = rawPacket.subarray(SESSION_KEY_START, SESSION_KEY_START + keyLength).toString("utf8"); - - // length of the session key should be 128 bytes - const sessionkeyString = Buffer.from(sessionKeyAsAscii, "hex"); - // Decrypt the sessionkey - const privatekeyContents = this.loadPrivateKeyContents(); - try { - - const decrypted = privateDecrypt( - { - key: privatekeyContents, - }, - sessionkeyString, - ); // length of decrypted should be 128 bytes - - const parsedSessionKey = parseNPSSessionKey(decrypted); - - this.sessionKey = parsedSessionKey.sessionKey; // length of session key should be 12 bytes - } catch (error: unknown) { - this.log.trace(`Session key: ${sessionkeyString.toString("utf8")}`); // 128 bytes - this.log.trace(`decrypted: ${this.sessionKey}`); // 12 bytes - this.log.fatal(`Error decrypting session key: ${(error as Error).message}`); - const err = new Error(`Error decrypting session key: ${(error as Error).message}`); - err.cause = error; - throw err; - } - } - - private loadPrivateKeyContents() { - if (this._config.privateKeyFile === "") { - throw Error("No private key file specified"); - } - const privatekeyContents = readFileSync(this._config.privateKeyFile); - return privatekeyContents; - } - - toJSON() { - this.log.debug("Returning as JSON"); - return { - msgNo: this._header.id, - msgLength: this._header.length, - content: this.data.toString("hex"), - contextId: this.contextId, - sessionKey: this.sessionKey, - rawBuffer: this.buffer.toString("hex"), - }; - } - - /** - * @return {string} - */ - dumpPacket(): string { - this.log.debug("Returning as string"); - let message = this._header.toString(); - message = message.concat( - `NPSUserStatus, + _config: Configuration; + log: ServerLogger; + sessionKey: string; + opCode: number; + contextId: string; + buffer: Buffer; + /** + * + * @param {Buffer} packet + * @param {Configuration} config + * @param {Serverogger} log + */ + constructor(packet: Buffer, config: Configuration, log: ServerLogger) { + super(); + this._config = config; + this.log = getServerLogger('NPSUserStatus'); + log.debug('Constructing NPSUserStatus'); + this._header._doDeserialize(packet); + this.sessionKey = ''; + + // Save the NPS opCode + this.opCode = packet.readInt16BE(0); + + // Save the contextId + this.contextId = packet.subarray(14, 48).toString(); + + // Save the raw packet + this.buffer = packet; + } + + /** + * Extracts the session key from the provided raw packet buffer. + * + * This method reads the session key length from the packet, extracts the session key, + * decrypts it using the private key, and parses the decrypted session key. + * + * @param rawPacket - The raw packet buffer containing the session key. + * + * @throws {Error} Throws an error if there is an issue decrypting the session key. + */ + extractSessionKeyFromPacket(rawPacket: Buffer): void { + this.log.debug('Extracting key'); + + const keyLength = rawPacket.readInt16LE(SESSION_KEY_START); + + // Extract the session key which is 128 acsii characters (256 bytes) + const sessionKeyAsAscii = rawPacket + .subarray(SESSION_KEY_START, SESSION_KEY_START + keyLength) + .toString('utf8'); + + // length of the session key should be 128 bytes + const sessionkeyString = Buffer.from(sessionKeyAsAscii, 'hex'); + // Decrypt the sessionkey + const privatekeyContents = this.loadPrivateKeyContents(); + try { + const decrypted = privateDecrypt( + { + key: privatekeyContents, + }, + sessionkeyString, + ); // length of decrypted should be 128 bytes + + const parsedSessionKey = parseNPSSessionKey(decrypted); + + this.sessionKey = parsedSessionKey.sessionKey; // length of session key should be 12 bytes + } catch (error: unknown) { + this.log.trace(`Session key: ${sessionkeyString.toString('utf8')}`); // 128 bytes + this.log.trace(`decrypted: ${this.sessionKey}`); // 12 bytes + this.log.fatal( + `Error decrypting session key: ${(error as Error).message}`, + ); + const err = new Error( + `Error decrypting session key: ${(error as Error).message}`, + ); + err.cause = error; + throw err; + } + } + + private loadPrivateKeyContents() { + if (this._config.privateKeyFile === '') { + throw Error('No private key file specified'); + } + const privatekeyContents = readFileSync(this._config.privateKeyFile); + return privatekeyContents; + } + + toJSON() { + this.log.debug('Returning as JSON'); + return { + msgNo: this._header.id, + msgLength: this._header.length, + content: this.data.toString('hex'), + contextId: this.contextId, + sessionKey: this.sessionKey, + rawBuffer: this.buffer.toString('hex'), + }; + } + + /** + * @return {string} + */ + dumpPacket(): string { + this.log.debug('Returning as string'); + let message = this._header.toString(); + message = message.concat( + `NPSUserStatus, ${JSON.stringify({ - contextId: this.contextId, - sessionkey: this.sessionKey, - })}`, - ); - return message; - } + contextId: this.contextId, + sessionkey: this.sessionKey, + })}`, + ); + return message; + } } /** @@ -156,14 +158,18 @@ export class NPSUserStatus extends LegacyMessage { * @returns An object containing the session key length, session key (in hex format), and expiration time. */ export function parseNPSSessionKey(buffer: Buffer): NPSSessionKey { -try { - const sessionKeyLength = buffer.readInt16BE(0); - const sessionKey = buffer.subarray(2, sessionKeyLength + 2).toString("hex"); - const expires = buffer.readInt32BE(sessionKeyLength + 2); - return { sessionKeyLength, sessionKey, expires }; -} catch (error) { - const err = new Error(`Error parsing session key: ${(error as Error).message}`); - err.cause = error; - throw err; -} + try { + const sessionKeyLength = buffer.readInt16BE(0); + const sessionKey = buffer + .subarray(2, sessionKeyLength + 2) + .toString('hex'); + const expires = buffer.readInt32BE(sessionKeyLength + 2); + return { sessionKeyLength, sessionKey, expires }; + } catch (error) { + const err = new Error( + `Error parsing session key: ${(error as Error).message}`, + ); + err.cause = error; + throw err; + } } diff --git a/packages/login/src/handleLoginData.ts b/packages/login/src/handleLoginData.ts index 8aee24b4b..64aa6ffb4 100644 --- a/packages/login/src/handleLoginData.ts +++ b/packages/login/src/handleLoginData.ts @@ -1,70 +1,58 @@ -import { - NPSMessage, - ServerLogger, -} from "rusty-motors-shared"; -import { messageHandlers } from "./internal.js"; -import { getServerLogger } from "rusty-motors-shared"; -import { GamePacket } from "rusty-motors-shared-packets"; -import { BytableMessage } from "@rustymotors/binary"; +import { NPSMessage } from 'rusty-motors-shared'; +import { getMessageHandlerOrFallback } from './internal.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; +import { GamePacket } from 'rusty-motors-shared-packets'; +import { BytableMessage } from '@rustymotors/binary'; -const defaultLogger = getServerLogger("LoginServer"); +const defaultLogger = getServerLogger('LoginServer'); /** - * Handles the reception of login data, deserializes the incoming message, and processes it. + * Processes an incoming login data message by deserializing it and invoking the appropriate handler. * - * @param {Object} params - The parameters for the function. - * @param {string} params.connectionId - The ID of the connection. - * @param {GamePacket} params.message - The serialized message buffer. - * @param {ServerLogger} [params.log=defaultLogger] - Optional logger instance. - * @returns {Promise<{ - * connectionId: string, - * messages: GamePacket[], - * }>} - The response from the login data handler. - * @throws {Error} - Throws an error if there is an issue processing the login data. + * @param connectionId - Unique identifier for the connection. + * @param message - The serialized login data message to process. + * @returns An object containing the connection ID and an array of resulting {@link GamePacket} messages. + * + * @throws {Error} If an error occurs during message handling or processing. */ export async function handleLoginData({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: BytableMessage; - log?: ServerLogger; + connectionId: string; + message: BytableMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: GamePacket[]; + connectionId: string; + messages: GamePacket[]; }> { - log.debug(`[${connectionId}] Entering handleLoginData`); - - // The packet needs to be an NPSMessage - const inboundMessage = new NPSMessage(); - inboundMessage._doDeserialize(message.serialize()); + log.debug(`[${connectionId}] Entering handleLoginData`); - const supportedHandler = messageHandlers.find((h) => { - return h.opCode === inboundMessage._header.id; - }); + // The packet needs to be an NPSMessage + const inboundMessage = new NPSMessage(); + inboundMessage.deserialize(message.serialize()); + let messageHandler; - if (typeof supportedHandler === "undefined") { - // We do not yet support this message code - throw Error( - `[${connectionId}] UNSUPPORTED_MESSAGECODE: ${inboundMessage._header.id}`, - ); - } + messageHandler = getMessageHandlerOrFallback(message.header.messageId); - try { - const result = await supportedHandler.handler({ - connectionId, - message, - log, - }); - log.debug( - `[${connectionId}] Leaving handleLoginData with ${result.messages.length} messages`, - ); - return result; - } catch (error) { - const err = Error(`[${connectionId}] Error in login service`, { - cause: error, - }); - throw err; - } + try { + const result = await messageHandler({ + connectionId, + message, + log, + }); + log.debug( + `[${connectionId}] Leaving handleLoginData with ${result.messages.length} messages`, + ); + return { + connectionId, + messages: result.messages, + }; + } catch (error) { + const err = Error(`[${connectionId}] Error in login service`, { + cause: error, + }); + throw err; + } } diff --git a/packages/login/src/internal.ts b/packages/login/src/internal.ts index bb0986c74..9bc584479 100644 --- a/packages/login/src/internal.ts +++ b/packages/login/src/internal.ts @@ -14,17 +14,32 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { ServerLogger } from "rusty-motors-shared"; -import { login } from "./login.js"; -import { GamePacket } from "rusty-motors-shared-packets"; -import { BytableMessage } from "@rustymotors/binary"; +import { login } from './login.js'; +import { GamePacket } from 'rusty-motors-shared-packets'; +import { BytableMessage } from '@rustymotors/binary'; +import { ServerLogger } from 'rusty-motors-logger'; + +export type LoginMessageHandlerFn = (args: { + connectionId: string; + message: BytableMessage; + log: ServerLogger; +}) => Promise<{ + connectionId: string; + messages: GamePacket[]; +}>; + +export type LoginMessageHandlerEntry = { + opCode: number; + name: string; + handler: LoginMessageHandlerFn; +}; /** * An array of message handlers for processing different types of messages. * Each handler is associated with an operation code (opCode) and a name. - * - * @type {Array<{opCode: number, name: string, handler: function}>} - * + * + * @type {LoginMessageHandlerEntry} + * * @property {number} opCode - The operation code that identifies the type of message. * @property {string} name - The name of the message handler. * @property {function} handler - The function that processes the message. It takes an object with the following properties: @@ -33,21 +48,52 @@ import { BytableMessage } from "@rustymotors/binary"; * @property {ServerLogger} handler.args.log - The logger for server logging. * @returns {Promise<{connectionId: string, messages: GamePacket[]}>} - A promise that resolves to an object containing the connection ID and an array of messages. */ -export const messageHandlers: { - opCode: number; - name: string; - handler: (args: { - connectionId: string; - message: BytableMessage; - log: ServerLogger; - }) => Promise<{ - connectionId: string; - messages: GamePacket[]; - }>; -}[] = [ - { - opCode: 1281, // 0x0501 - name: "UserLogin", - handler: login, - }, +const messageHandlers: LoginMessageHandlerEntry[] = [ + { + opCode: 1281, // 0x0501 + name: 'UserLogin', + handler: login, + }, ]; + +/** + * Handles messages with unrecognized operation codes during login. + * + * Logs an error and throws if no handler exists for the message's {@link BytableMessage.header.messageId}. + * + * @throws {Error} Always thrown to indicate that no handler was found for the given message operation code. + */ +function loginMessageHandlerFallback({ + connectionId, + message, + log, +}: { + connectionId: string; + message: BytableMessage; + log: ServerLogger; +}): Promise<{ + connectionId: string; + messages: GamePacket[]; +}> { + log.error( + `[${connectionId}] No handler found for message with opCode: ${message.header.messageId}`, + ); + throw new Error( + `No handler found for message with opCode: ${message.header.messageId}`, + ); +} + +/** + * Retrieves the login message handler function for a given operation code, or returns a fallback handler if none is found. + * + * @param opCode - The operation code identifying the message type. + * @returns The corresponding login message handler function, or a fallback that throws if the code is unrecognized. + */ +export function getMessageHandlerOrFallback( + opCode: number, +): LoginMessageHandlerFn { + return ( + messageHandlers.find((h) => h.opCode === opCode)?.handler ?? + loginMessageHandlerFallback + ); +} diff --git a/packages/login/src/login.ts b/packages/login/src/login.ts index f1e586d81..1fd16087e 100644 --- a/packages/login/src/login.ts +++ b/packages/login/src/login.ts @@ -1,120 +1,118 @@ -import { databaseManager, findCustomerByContext } from "rusty-motors-database"; -import { getServerConfiguration, NetworkMessage } from "rusty-motors-shared"; -import { NPSUserStatus } from "./NPSUserStatus.js"; -import { ServerLogger, getServerLogger } from "rusty-motors-shared"; -import { GamePacket } from "rusty-motors-shared-packets"; -import { BytableMessage } from "@rustymotors/binary"; - +import { databaseService, } from 'rusty-motors-database'; +import { getServerConfiguration, NetworkMessage } from 'rusty-motors-shared'; +import { NPSUserStatus } from './NPSUserStatus.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; +import { GamePacket } from 'rusty-motors-shared-packets'; +import { BytableMessage } from '@rustymotors/binary'; /** - * Process a UserLogin packet - * @private - * @param {object} args - * @param {string} args.connectionId - * @param {SerializedBufferOld} args.message - * @param {ServerLogger} [args.log=getServerLogger({ name: "LoginServer" })] - * @returns {Promise<{ - * connectionId: string, - * messages: SerializedBufferOld[], - * }>} + * Processes a user login packet, validates the user, updates the session key, and returns the login response messages. + * + * Attempts to locate the user record by context ID, updates the session key in the database, and constructs the outbound login response. Returns an object containing the connection ID and two identical login response messages. + * + * @param connectionId - Identifier for the connection. + * @param message - The incoming login message packet. + * @returns An object containing the {@link connectionId} and an array of two identical login response messages. + * + * @throws {Error} If the user record cannot be found for the provided context ID. + * @throws {Error} If updating the session key in the database fails. */ export async function login({ - connectionId, - message, - log = getServerLogger( "LoginServer"), + connectionId, + message, + log = getServerLogger('LoginServer'), }: { - connectionId: string; - message: BytableMessage; - log?: ServerLogger; + connectionId: string; + message: BytableMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: GamePacket[]; + connectionId: string; + messages: GamePacket[]; }> { - const data = message.serialize(); - - log.debug(`[${connectionId}] Entering login`); - - log.debug(`[${connectionId}] Creating NPSUserStatus object`); - const userStatus = new NPSUserStatus(data, getServerConfiguration(), log); - log.debug(`[${connectionId}] NPSUserStatus object created`); - - log.debug(`[${connectionId}] Extracting session key from packet`); - userStatus.extractSessionKeyFromPacket(data); - log.debug(`[${connectionId}] Session key extracted`); - - const { contextId, sessionKey } = userStatus; - - log.debug(`[${connectionId}] Context ID: ${contextId}`); - userStatus.dumpPacket(); - - // Load the customer record by contextId - const userRecord = findCustomerByContext(contextId); - - if (typeof userRecord === "undefined") { - // We were not able to locate the user's record - throw Error( - `[${connectionId}] Unable to locate user record for contextId: ${contextId}`, - ); - } - - // Save sessionkey in database under customerId - await databaseManager.updateSessionKey( - userRecord.customerId, - sessionKey ?? "", - contextId, - connectionId, - ).catch((error) => { - const err = Error( - `[${connectionId}] Error updating session key in the database`, - { cause: error }, - ); - throw err; - }); - - log.debug(`[${connectionId}] Creating outbound message`); - - const outboundMessage = new NetworkMessage(0x601); - - const dataBuffer = Buffer.alloc(26); - let offset = 0; - dataBuffer.writeInt32BE(userRecord.customerId, offset); - offset += 4; - dataBuffer.writeInt32BE(userRecord.profileId, offset); - offset += 4; - dataBuffer.writeInt8(0, offset); // isCacheHit - offset += 1; - dataBuffer.writeInt8(0, offset); // ban - offset += 1; - dataBuffer.writeInt8(0, offset); // gag - offset += 1; - dataBuffer.write(sessionKey ?? "", offset, 12, "ascii"); - - const packetContent = dataBuffer; - - // Set the packet content in the outbound message - outboundMessage.data = packetContent; - - log.debug( - `[${connectionId}] Outbound message: ${outboundMessage.toHexString()}`, - ); - - const outboundMessage2 = new GamePacket(); - outboundMessage2.deserialize(outboundMessage.serialize()); - - log.debug( - `[${connectionId}] Outbound message2: ${outboundMessage2.toHexString()}`, - ); - - // Update the data buffer - const response = { - connectionId, - messages: [outboundMessage2, outboundMessage2], - }; - log.debug( - `[${connectionId}] Leaving login with ${response.messages.length} messages`, - ); - return response; + const data = message.serialize(); + + log.debug(`[${connectionId}] Entering login`); + + log.debug(`[${connectionId}] Creating NPSUserStatus object`); + const userStatus = new NPSUserStatus(data, getServerConfiguration(), log); + log.debug(`[${connectionId}] NPSUserStatus object created`); + + log.debug(`[${connectionId}] Extracting session key from packet`); + userStatus.extractSessionKeyFromPacket(data); + log.debug(`[${connectionId}] Session key extracted`); + + const { contextId, sessionKey } = userStatus; + + log.debug(`[${connectionId}] Context ID: ${contextId}`); + userStatus.dumpPacket(); + + // Load the customer record by contextId + const userRecord = await databaseService.findSessionByContext(contextId); + + if (typeof userRecord === 'undefined') { + // We were not able to locate the user's record + throw Error( + `[${connectionId}] Unable to locate user record for contextId: ${contextId}`, + ); + } + + // Save sessionkey in database under customerId + await databaseService + .updateSessionKey( + userRecord.customerId, + sessionKey ?? '', + contextId, + connectionId, + ) + .catch((error) => { + const err = Error( + `[${connectionId}] Error updating session key in the database`, + { cause: error }, + ); + throw err; + }); + + log.debug(`[${connectionId}] Creating outbound message`); + + const outboundMessage = new NetworkMessage(0x601); + + const dataBuffer = Buffer.alloc(26); + let offset = 0; + dataBuffer.writeInt32BE(userRecord.customerId, offset); + offset += 4; + dataBuffer.writeInt32BE(userRecord.profileId, offset); + offset += 4; + dataBuffer.writeInt8(0, offset); // isCacheHit + offset += 1; + dataBuffer.writeInt8(0, offset); // ban + offset += 1; + dataBuffer.writeInt8(0, offset); // gag + offset += 1; + dataBuffer.write(sessionKey ?? '', offset, 12, 'ascii'); + + const packetContent = dataBuffer; + + // Set the packet content in the outbound message + outboundMessage.data = packetContent; + + log.debug( + `[${connectionId}] Outbound message: ${outboundMessage.toHexString()}`, + ); + + const outboundMessage2 = new GamePacket(); + outboundMessage2.deserialize(outboundMessage.serialize()); + + log.debug( + `[${connectionId}] Outbound message2: ${outboundMessage2.toHexString()}`, + ); + + // Update the data buffer + const response = { + connectionId, + messages: [outboundMessage2, outboundMessage2], + }; + log.debug( + `[${connectionId}] Leaving login with ${response.messages.length} messages`, + ); + return response; } - - - diff --git a/packages/login/src/receiveLoginData.ts b/packages/login/src/receiveLoginData.ts index b3e3269ce..878a86097 100644 --- a/packages/login/src/receiveLoginData.ts +++ b/packages/login/src/receiveLoginData.ts @@ -13,69 +13,80 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { - ServerLogger, - type ServiceResponse, -} from "rusty-motors-shared"; -import { handleLoginData } from "./handleLoginData.js"; -import { BufferSerializer, GamePacket } from "rusty-motors-shared-packets"; -import { getServerLogger } from "rusty-motors-shared"; -import { BytableMessage } from "@rustymotors/binary"; - +import { type ServiceResponse } from 'rusty-motors-shared'; +import { handleLoginData } from './handleLoginData.js'; +import { BufferSerializer, GamePacket } from 'rusty-motors-shared-packets'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; +import { BytableMessage } from '@rustymotors/binary'; /** - * Handles the reception of login data, deserializes the incoming message, and processes it. + * Processes incoming login data for a connection and returns the resulting service response. + * + * Awaits handling of the login data, logs entry and exit, and converts the response messages to the required serialization format before returning. + * + * @param connectionId - Unique identifier for the connection. + * @param message - Serialized login data message. + * @param log - Optional logger instance. + * @returns A promise resolving to the processed service response with messages converted to {@link BufferSerializer} format. * - * @param {Object} params - The parameters for the function. - * @param {string} params.connectionId - The ID of the connection. - * @param {BufferSerializer} params.message - The serialized message buffer. - * @param {ServerLogger} [params.log=getServerLogger("receiveLoginData")] - Optional logger instance. - * @returns {Promise} - The response from the login data handler. - * @throws {Error} - Throws an error if there is an issue processing the login data. + * @throws {Error} If an error occurs during login data processing, including the connection ID in the error message. + * + * @remark The conversion of response messages is a temporary workaround for legacy serialization compatibility. */ export async function receiveLoginData({ - connectionId, - message, - log = getServerLogger("receiveLoginData"), + connectionId, + message, + log = getServerLogger('receiveLoginData'), }: { - connectionId: string; - message: BytableMessage; - log?: ServerLogger; + connectionId: string; + message: BytableMessage; + log?: ServerLogger; }): Promise { - try { - log.debug(`[${connectionId}] Entering login module`); - const response = await handleLoginData({ - connectionId, - message, - log, - }); - log.debug( - `[${connectionId}] Exiting login module ${response.messages.length} messages`, - ); + try { + log.debug(`[${connectionId}] Entering login module`); + const response = await handleLoginData({ + connectionId, + message, + log, + }); + log.debug( + `[${connectionId}] Exiting login module ${response.messages.length} messages`, + ); - // @ts-ignore-next-line - This is a temporary workaround for the old serialization format - response.messages = GamePacketArrayToBufferSerializerArray(response.messages); + // @ts-expect-error-next-line - This is a temporary workaround for the old serialization format + response.messages = GamePacketArrayToBufferSerializerArray( + response.messages, + ); - // @ts-ignore-next-line - This is a temporary workaround for the old serialization format - return response; - } catch (error) { - const err = new Error( - `[${connectionId}] Error in login service: ${(error as Error).message}`, - { cause: error }, - ); - throw err; - } + // @ts-expect-error-next-line - This is a temporary workaround for the old serialization format + return response; + } catch (error) { + const err = new Error( + `[${connectionId}] Error in login service: ${(error as Error).message}`, + { cause: error }, + ); + throw err; + } } +/** + * Converts an array of {@link GamePacket} objects into an array of {@link BufferSerializer} instances. + * + * Each packet is serialized and then deserialized into a new {@link BufferSerializer}. + * + * @param packets - The array of game packets to convert. + * @returns An array of {@link BufferSerializer} objects representing the serialized packets. + * + * @remark This conversion is a temporary workaround for compatibility with an older serialization format. + */ function GamePacketArrayToBufferSerializerArray( - packets: GamePacket[], + packets: GamePacket[], ): BufferSerializer[] { - let bufferSerializers: BufferSerializer[] = []; - for (const packet of packets) { - const bufferSerializer = new BufferSerializer(); - bufferSerializer.deserialize(packet.serialize()); - bufferSerializers.push(bufferSerializer); - } - return bufferSerializers; + let bufferSerializers: BufferSerializer[] = []; + for (const packet of packets) { + const bufferSerializer = new BufferSerializer(); + bufferSerializer.deserialize(packet.serialize()); + bufferSerializers.push(bufferSerializer); + } + return bufferSerializers; } - diff --git a/packages/nps/gameMessageProcessors/getLobMiniRiffList.ts b/packages/nps/gameMessageProcessors/getLobMiniRiffList.ts index 1be0c6350..4dc36c039 100644 --- a/packages/nps/gameMessageProcessors/getLobMiniRiffList.ts +++ b/packages/nps/gameMessageProcessors/getLobMiniRiffList.ts @@ -1,40 +1,42 @@ import { - GameMessage, - MiniRiffInfo, - MiniRiffList, - getAsHex, -} from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; + GameMessage, + MiniRiffInfo, + MiniRiffList, + getAsHex, +} from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.getLobMiniRiffList"); +const defaultLogger = getServerLogger('nps.getLobMiniRiffList'); // Command id: 0x30c export async function getLobMiniRiffList( - _commandId: number, - data: Buffer, + _commandId: number, + data: Buffer, ): Promise { - defaultLogger.debug("getLobMiniRiffList called"); - defaultLogger.info(`Processing getLobMiniRiffList command: ${getAsHex(data)}`); + defaultLogger.debug('getLobMiniRiffList called'); + defaultLogger.info( + `Processing getLobMiniRiffList command: ${getAsHex(data)}`, + ); - const riffList = new MiniRiffList(); + const riffList = new MiniRiffList(); - riffList.addRiff(new MiniRiffInfo("CTRL", 0, 1)); - riffList.addRiff(new MiniRiffInfo("MC141", 141, 0)); - riffList.addRiff(new MiniRiffInfo("MCCHAT", 191, 0)); + riffList.addRiff(new MiniRiffInfo('CTRL', 0, 1)); + riffList.addRiff(new MiniRiffInfo('MC141', 141, 0)); + riffList.addRiff(new MiniRiffInfo('MCCHAT', 191, 0)); - defaultLogger.info(`getLobMiniRiffList: ${riffList.toString()}`); + defaultLogger.info(`getLobMiniRiffList: ${riffList.toString()}`); - const responseMessage = new GameMessage(0); - responseMessage.header.setId(0x404); - responseMessage.setData(riffList); + const responseMessage = new GameMessage(0); + responseMessage.header.setId(0x404); + responseMessage.setData(riffList); - defaultLogger.info("Dumping responseMessage: "); + defaultLogger.info('Dumping responseMessage: '); - defaultLogger.info( - `responseMessage: ${ - responseMessage.serialize().length - } bytes - ${getAsHex(responseMessage.serialize())}`, - ); + defaultLogger.info( + `responseMessage: ${ + responseMessage.serialize().length + } bytes - ${getAsHex(responseMessage.serialize())}`, + ); - return responseMessage.serialize(); + return responseMessage.serialize(); } diff --git a/packages/nps/gameMessageProcessors/getLobMiniUserList.ts b/packages/nps/gameMessageProcessors/getLobMiniUserList.ts index 2c37c9c23..868f7c1bf 100644 --- a/packages/nps/gameMessageProcessors/getLobMiniUserList.ts +++ b/packages/nps/gameMessageProcessors/getLobMiniUserList.ts @@ -1,28 +1,30 @@ import { - GameMessage, - MiniUserInfo, - MiniUserList, - getAsHex, -} from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; + GameMessage, + MiniUserInfo, + MiniUserList, + getAsHex, +} from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.getLobMiniUserList"); +const defaultLogger = getServerLogger('nps.getLobMiniUserList'); // Command id: 0x128 export async function getLobMiniUserList( - _commandId: number, - data: Buffer, + _commandId: number, + data: Buffer, ): Promise { - defaultLogger.debug("getLobMiniUserList called"); - defaultLogger.info(`Processing getLobMiniUserList command: ${getAsHex(data)}`); + defaultLogger.debug('getLobMiniUserList called'); + defaultLogger.info( + `Processing getLobMiniUserList command: ${getAsHex(data)}`, + ); - const miniUserList = new MiniUserList(0); + const miniUserList = new MiniUserList(0); - miniUserList.addChannelUser(new MiniUserInfo(1000, "Molly")); + miniUserList.addChannelUser(new MiniUserInfo(1000, 'Molly')); - const responseMessage = new GameMessage(0); - responseMessage.header.setId(0x229); - responseMessage.setData(miniUserList); + const responseMessage = new GameMessage(0); + responseMessage.header.setId(0x229); + responseMessage.setData(miniUserList); - return Promise.resolve(responseMessage.serialize()); + return Promise.resolve(responseMessage.serialize()); } diff --git a/packages/nps/gameMessageProcessors/processCheckPlateText.ts b/packages/nps/gameMessageProcessors/processCheckPlateText.ts index c7981f18c..9076f9231 100644 --- a/packages/nps/gameMessageProcessors/processCheckPlateText.ts +++ b/packages/nps/gameMessageProcessors/processCheckPlateText.ts @@ -1,27 +1,31 @@ -import { GameMessage } from "../messageStructs/GameMessage.js"; -import type { UserStatus } from "../messageStructs/UserStatus.js"; -import { getLenString } from "../src/utils/pureGet.js"; -import { sendNPSAck } from "../src/utils/sendNPSAck.js"; -import type { GameSocketCallback } from "./index.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { GameMessage } from '../messageStructs/GameMessage.js'; +import type { UserStatus } from '../messageStructs/UserStatus.js'; +import { getLenString } from '../src/utils/pureGet.js'; +import { sendNPSAck } from '../src/utils/sendNPSAck.js'; +import type { GameSocketCallback } from './index.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processCheckPlateText"); +const defaultLogger = getServerLogger('nps.processCheckPlateText'); export async function processCheckPlateText( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.info("processCheckPlateText called"); - const plateType = message.getDataAsBuffer().readUInt32BE(0); + defaultLogger.info('processCheckPlateText called'); + const plateType = message.getDataAsBuffer().readUInt32BE(0); - const requestedPlateText = getLenString(message.getDataAsBuffer(), 4, false); + const requestedPlateText = getLenString( + message.getDataAsBuffer(), + 4, + false, + ); - defaultLogger.info( - `Requested plate text: ${requestedPlateText} for plate type ${plateType}`, - ); + defaultLogger.info( + `Requested plate text: ${requestedPlateText} for plate type ${plateType}`, + ); - sendNPSAck(socketCallback); - return Promise.resolve(); + sendNPSAck(socketCallback); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processCheckProfileName.ts b/packages/nps/gameMessageProcessors/processCheckProfileName.ts index 3993edaaa..0f7f472e4 100644 --- a/packages/nps/gameMessageProcessors/processCheckProfileName.ts +++ b/packages/nps/gameMessageProcessors/processCheckProfileName.ts @@ -1,32 +1,32 @@ -import { GameMessage } from "../messageStructs/GameMessage.js"; -import { getLenString } from "../src/utils/pureGet.js"; -import type { GameSocketCallback } from "./index.js"; +import { GameMessage } from '../messageStructs/GameMessage.js'; +import { getLenString } from '../src/utils/pureGet.js'; +import type { GameSocketCallback } from './index.js'; -import type { UserStatus } from "../messageStructs/UserStatus.js"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from '../messageStructs/UserStatus.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processCheckProfileName"); +const defaultLogger = getServerLogger('nps.processCheckProfileName'); export async function processCheckProfileName( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.info("processCheckProfileName called"); - const customerId = message.serialize().readUInt32BE(8); + defaultLogger.info('processCheckProfileName called'); + const customerId = message.serialize().readUInt32BE(8); - const requestedPersonaName = getLenString(message.serialize(), 12, false); + const requestedPersonaName = getLenString(message.serialize(), 12, false); - defaultLogger.info( - `Requested persona name: ${requestedPersonaName} for customer ${customerId}`, - ); + defaultLogger.info( + `Requested persona name: ${requestedPersonaName} for customer ${customerId}`, + ); - const response = new GameMessage(0); - response.header.setId(0x601); + const response = new GameMessage(0); + response.header.setId(0x601); - const responseBytes = response.serialize(); + const responseBytes = response.serialize(); - socketCallback([responseBytes]); - return Promise.resolve(); + socketCallback([responseBytes]); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processCreateProfile.ts b/packages/nps/gameMessageProcessors/processCreateProfile.ts index 244175ce0..d8ddbcb3f 100644 --- a/packages/nps/gameMessageProcessors/processCreateProfile.ts +++ b/packages/nps/gameMessageProcessors/processCreateProfile.ts @@ -1,35 +1,39 @@ -import { GameMessage } from "../messageStructs/GameMessage.js"; -import { GameProfile } from "../messageStructs/GameProfile.js"; -import type { UserStatus } from "../messageStructs/UserStatus.js"; -import type { GameSocketCallback } from "./index.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { GameMessage } from '../messageStructs/GameMessage.js'; +import { GameProfile } from '../messageStructs/GameProfile.js'; +import type { UserStatus } from '../messageStructs/UserStatus.js'; +import type { GameSocketCallback } from './index.js'; +import { getServerLogger } from 'rusty-motors-logger'; export async function processCreateProfile( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - const defaultLogger = getServerLogger("nps.processCreateProfile"); - // Log the request - defaultLogger.info(`ProcessCreateProfile request: ${message.toString()}`); + const defaultLogger = getServerLogger('nps.processCreateProfile'); + // Log the request + defaultLogger.info(`ProcessCreateProfile request: ${message.toString()}`); - const createProfileMessage = GameProfile.fromBytes(message.getDataAsBuffer()); + const createProfileMessage = GameProfile.fromBytes( + message.getDataAsBuffer(), + ); - // Log the request - defaultLogger.info(`ProcessCreateProfile request: ${createProfileMessage.toString()}`); + // Log the request + defaultLogger.info( + `ProcessCreateProfile request: ${createProfileMessage.toString()}`, + ); - // TODO: Add the profile + // TODO: Add the profile - // TODO: Send the response - const response = new GameMessage(257); - response.header.setId(0x601); + // TODO: Send the response + const response = new GameMessage(257); + response.header.setId(0x601); - response.setData(message.getData()); + response.setData(message.getData()); - // Log the response - defaultLogger.info(`ProcessCreateProfile response: ${response.toString()}`); + // Log the response + defaultLogger.info(`ProcessCreateProfile response: ${response.toString()}`); - socketCallback([response.serialize()]); - return Promise.resolve(); + socketCallback([response.serialize()]); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processDeleteProfile.ts b/packages/nps/gameMessageProcessors/processDeleteProfile.ts index 2d727d222..bf56c0ab7 100644 --- a/packages/nps/gameMessageProcessors/processDeleteProfile.ts +++ b/packages/nps/gameMessageProcessors/processDeleteProfile.ts @@ -1,108 +1,108 @@ -import crypto from "node:crypto"; -import fs from "node:fs"; -import { GameMessage } from "../messageStructs/GameMessage.js"; -import { SessionKey } from "../messageStructs/SessionKey.js"; -import { getLenString } from "../src/utils/pureGet.js"; -import type { GameSocketCallback } from "./index.js"; +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import { GameMessage } from '../messageStructs/GameMessage.js'; +import { SessionKey } from '../messageStructs/SessionKey.js'; +import { getLenString } from '../src/utils/pureGet.js'; +import type { GameSocketCallback } from './index.js'; -import type { UserStatus } from "../messageStructs/UserStatus.js"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from '../messageStructs/UserStatus.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processDeleteProfile"); +const defaultLogger = getServerLogger('nps.processDeleteProfile'); export function loadPrivateKey(path: string): string { - const privateKey = fs.readFileSync(path); + const privateKey = fs.readFileSync(path); - return privateKey.toString("utf8"); + return privateKey.toString('utf8'); } export function decryptSessionKey( - encryptedSessionKey: string, - privateKey: string, + encryptedSessionKey: string, + privateKey: string, ): string { - const sessionKeyStructure = crypto.privateDecrypt( - privateKey, - Buffer.from(encryptedSessionKey, "hex"), - ); + const sessionKeyStructure = crypto.privateDecrypt( + privateKey, + Buffer.from(encryptedSessionKey, 'hex'), + ); - return sessionKeyStructure.toString("hex"); + return sessionKeyStructure.toString('hex'); } export function unpackUserLoginMessage(message: GameMessage): { - sessionKey: string; - gameId: string; - contextToken: string; + sessionKey: string; + gameId: string; + contextToken: string; } { - // Get the context token - const ticket = getLenString(message.getDataAsBuffer(), 0, false); + // Get the context token + const ticket = getLenString(message.getDataAsBuffer(), 0, false); - let dataOffset = ticket.length + 2; + let dataOffset = ticket.length + 2; - // The next data structure is a container with an empty id, a length, and a data structure + // The next data structure is a container with an empty id, a length, and a data structure - // Skip the empty id - dataOffset += 2; + // Skip the empty id + dataOffset += 2; - // Get the next data length - const nextDataLength = message.getDataAsBuffer().readUInt16BE(dataOffset); + // Get the next data length + const nextDataLength = message.getDataAsBuffer().readUInt16BE(dataOffset); - // This value is the encrypted session key hex, stored as a string - const encryptedSessionKey = message - .getDataAsBuffer() - .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength) - .toString("utf8"); + // This value is the encrypted session key hex, stored as a string + const encryptedSessionKey = message + .getDataAsBuffer() + .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength) + .toString('utf8'); - // Load the private key - const privateKey = loadPrivateKey("./data/private_key.pem"); + // Load the private key + const privateKey = loadPrivateKey('./data/private_key.pem'); - // Decrypt the session key - const sessionKey = decryptSessionKey(encryptedSessionKey, privateKey); + // Decrypt the session key + const sessionKey = decryptSessionKey(encryptedSessionKey, privateKey); - // Unpack the session key - const sessionKeyStructure = SessionKey.fromBytes( - Buffer.from(sessionKey, "hex"), - ); + // Unpack the session key + const sessionKeyStructure = SessionKey.fromBytes( + Buffer.from(sessionKey, 'hex'), + ); - // Update the data offset - dataOffset += 2 + nextDataLength; + // Update the data offset + dataOffset += 2 + nextDataLength; - // Get the next data length - const nextDataLength2 = message.getDataAsBuffer().readUInt16BE(dataOffset); + // Get the next data length + const nextDataLength2 = message.getDataAsBuffer().readUInt16BE(dataOffset); - // This value is the game id (used by server to identify the game) - const gameId = message - .getDataAsBuffer() - .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength2) - .toString("utf8"); + // This value is the game id (used by server to identify the game) + const gameId = message + .getDataAsBuffer() + .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength2) + .toString('utf8'); - // Update the data offset - dataOffset += 2 + nextDataLength2; + // Update the data offset + dataOffset += 2 + nextDataLength2; - // Return the session key, game id, and context token - return { - sessionKey: sessionKeyStructure.getKey(), - gameId, - contextToken: ticket, - }; + // Return the session key, game id, and context token + return { + sessionKey: sessionKeyStructure.getKey(), + gameId, + contextToken: ticket, + }; } export async function processDeleteProfile( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.debug("processDeleteProfile called"); - // Log the message - defaultLogger.info(`Delete profile request: ${message.toString()}`); + defaultLogger.debug('processDeleteProfile called'); + // Log the message + defaultLogger.info(`Delete profile request: ${message.toString()}`); - // TODO: Delete the profile + // TODO: Delete the profile - // Create a new message - Login ACK - const loginACK = new GameMessage(0); - loginACK.header.setId(0x60c); + // Create a new message - Login ACK + const loginACK = new GameMessage(0); + loginACK.header.setId(0x60c); - // Send the ack - socketCallback([loginACK.serialize()]); - return Promise.resolve(); + // Send the ack + socketCallback([loginACK.serialize()]); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processEncryptedGameCommand.ts b/packages/nps/gameMessageProcessors/processEncryptedGameCommand.ts index 0147d4dc3..ce6a9ac1f 100644 --- a/packages/nps/gameMessageProcessors/processEncryptedGameCommand.ts +++ b/packages/nps/gameMessageProcessors/processEncryptedGameCommand.ts @@ -1,113 +1,124 @@ import { - GameMessage, - SerializableData, -} from "../messageStructs/GameMessage.js"; + GameMessage, + SerializableData, +} from '../messageStructs/GameMessage.js'; import { - type EncryptionSession, - getEncryptionSession, - newEncryptionSession, - setEncryptionSession, -} from "../src/EncryptionSession.js"; -import { getAsHex } from "../src/utils/pureGet.js"; -import type { GameSocketCallback } from "./index.js"; -import { lobbyCommandMap } from "./lobbyCommands.js"; + type EncryptionSession, + getEncryptionSession, + newEncryptionSession, + setEncryptionSession, +} from '../src/EncryptionSession.js'; +import { getAsHex } from '../src/utils/pureGet.js'; +import type { GameSocketCallback } from './index.js'; +import { lobbyCommandMap } from './lobbyCommands.js'; -import type { UserStatus } from "../messageStructs/UserStatus.js"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from '../messageStructs/UserStatus.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processEncryptedGameCommand"); +const defaultLogger = getServerLogger('nps.processEncryptedGameCommand'); export async function processEncryptedGameCommand( - connectionId: string, - userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + connectionId: string, + userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.debug("processEncryptedGameCommand called"); - defaultLogger.info(`Attempting to decrypt message: ${message.toString()}`); - - // Get the encryption session - let encryptionSession: EncryptionSession | undefined = - getEncryptionSession(connectionId); - - // If the encryption session doesn't exist, attempt to create it - if (typeof encryptionSession === "undefined") { - try { - // Create the encryption session - const newSession = newEncryptionSession({ - connectionId, - customerId: userStatus.getCustomerId(), - sessionKey: userStatus.getSessionKey().getKey().substring(0, 16), - }); - setEncryptionSession(newSession); - encryptionSession = newSession; - } catch (error) { - defaultLogger.error(`Error creating encryption session: ${error as string}`); - throw new Error("Error creating encryption session"); - } - - // Log the encryption session - defaultLogger.info(`Created encryption session for ${userStatus.getCustomerId()}`); - } - - // Attempt to decrypt the message - const decryptedbytes = encryptionSession.gameDecipher.update( - message.getDataAsBuffer(), - ); - - // Log the decrypted bytes - defaultLogger.info(`Decrypted bytes: ${getAsHex(decryptedbytes)}`); - - // Set the decrypted bytes as a new message - const decryptedMessage = new GameMessage(0); - decryptedMessage.deserialize(decryptedbytes); - - // Log the decrypted message id - defaultLogger.info(`Decrypted message ID: ${decryptedMessage.header.getId()}`); - - // Do we have a valid message processor? - const processor = lobbyCommandMap.get(decryptedMessage.header.getId()); - - if (typeof processor === "undefined") { - const err = `No processor found for message ID: ${decryptedMessage.header.getId()}`; - defaultLogger.fatal(err); - throw Error(err); - } - - // Process the message - const response = await processor( - decryptedMessage.header.getId(), - decryptedMessage.getDataAsBuffer(), - ); - - // Log the response - defaultLogger.info(`Response: ${response.length} bytes, ${getAsHex(response)}`); - - // Encrypt the response - const encryptedResponse = encryptionSession.gameCipher.update(response); - setEncryptionSession(encryptionSession); - - // Log the encrypted response - defaultLogger.info( - `Encrypted response: ${encryptedResponse.length} bytes, ${getAsHex( - encryptedResponse, - )}`, - ); - - const responsePacket = new GameMessage(0); - responsePacket.header.setId(0x1101); - - const responseData = new SerializableData(encryptedResponse.length); - responseData.deserialize(encryptedResponse); - - responsePacket.setData(responseData); - defaultLogger.info( - `Response packet: ${responsePacket.header.getLength()} bytes, ${getAsHex( - responsePacket.serialize(), - )}`, - ); - const responseBytes = responsePacket.serialize(); - - socketCallback([responseBytes]); - return Promise.resolve(); + defaultLogger.debug('processEncryptedGameCommand called'); + defaultLogger.info(`Attempting to decrypt message: ${message.toString()}`); + + // Get the encryption session + let encryptionSession: EncryptionSession | undefined = + getEncryptionSession(connectionId); + + // If the encryption session doesn't exist, attempt to create it + if (typeof encryptionSession === 'undefined') { + try { + // Create the encryption session + const newSession = newEncryptionSession({ + connectionId, + customerId: userStatus.getCustomerId(), + sessionKey: userStatus + .getSessionKey() + .getKey() + .substring(0, 16), + }); + setEncryptionSession(newSession); + encryptionSession = newSession; + } catch (error) { + defaultLogger.error( + `Error creating encryption session: ${error as string}`, + ); + throw new Error('Error creating encryption session'); + } + + // Log the encryption session + defaultLogger.info( + `Created encryption session for ${userStatus.getCustomerId()}`, + ); + } + + // Attempt to decrypt the message + const decryptedbytes = encryptionSession.gameDecipher.update( + message.getDataAsBuffer(), + ); + + // Log the decrypted bytes + defaultLogger.info(`Decrypted bytes: ${getAsHex(decryptedbytes)}`); + + // Set the decrypted bytes as a new message + const decryptedMessage = new GameMessage(0); + decryptedMessage.deserialize(decryptedbytes); + + // Log the decrypted message id + defaultLogger.info( + `Decrypted message ID: ${decryptedMessage.header.getId()}`, + ); + + // Do we have a valid message processor? + const processor = lobbyCommandMap.get(decryptedMessage.header.getId()); + + if (typeof processor === 'undefined') { + const err = `No processor found for message ID: ${decryptedMessage.header.getId()}`; + defaultLogger.fatal(err); + throw Error(err); + } + + // Process the message + const response = await processor( + decryptedMessage.header.getId(), + decryptedMessage.getDataAsBuffer(), + ); + + // Log the response + defaultLogger.info( + `Response: ${response.length} bytes, ${getAsHex(response)}`, + ); + + // Encrypt the response + const encryptedResponse = encryptionSession.gameCipher.update(response); + setEncryptionSession(encryptionSession); + + // Log the encrypted response + defaultLogger.info( + `Encrypted response: ${encryptedResponse.length} bytes, ${getAsHex( + encryptedResponse, + )}`, + ); + + const responsePacket = new GameMessage(0); + responsePacket.header.setId(0x1101); + + const responseData = new SerializableData(encryptedResponse.length); + responseData.deserialize(encryptedResponse); + + responsePacket.setData(responseData); + defaultLogger.info( + `Response packet: ${responsePacket.header.getLength()} bytes, ${getAsHex( + responsePacket.serialize(), + )}`, + ); + const responseBytes = responsePacket.serialize(); + + socketCallback([responseBytes]); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processGameLogin.ts b/packages/nps/gameMessageProcessors/processGameLogin.ts index 11af98812..aeb85c965 100644 --- a/packages/nps/gameMessageProcessors/processGameLogin.ts +++ b/packages/nps/gameMessageProcessors/processGameLogin.ts @@ -1,207 +1,215 @@ -import crypto from "node:crypto"; -import fs from "node:fs"; -import * as Sentry from "@sentry/node"; -import { getServerConfiguration } from "rusty-motors-shared"; -import { GameMessage } from "../messageStructs/GameMessage.js"; -import { SessionKey } from "../messageStructs/SessionKey.js"; -import { UserStatus } from "../messageStructs/UserStatus.js"; -import { getToken } from "../services/token.js"; -import { UserStatusManager } from "../src/UserStatusManager.js"; -import { getAsHex, getLenString } from "../src/utils/pureGet.js"; -import type { ISerializable } from "../types.js"; -import type { GameSocketCallback } from "./index.js"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("nps.processGameLogin"); +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import * as Sentry from '@sentry/node'; +import { getServerConfiguration } from 'rusty-motors-shared'; +import { GameMessage } from '../messageStructs/GameMessage.js'; +import { SessionKey } from '../messageStructs/SessionKey.js'; +import { UserStatus } from '../messageStructs/UserStatus.js'; +import { getToken } from '../services/token.js'; +import { UserStatusManager } from '../src/UserStatusManager.js'; +import { getAsHex, getLenString } from '../src/utils/pureGet.js'; +import type { ISerializable } from '../types.js'; +import type { GameSocketCallback } from './index.js'; +import { getServerLogger } from 'rusty-motors-logger'; + +const defaultLogger = getServerLogger('nps.processGameLogin'); export function loadPrivateKey(path: string): string { - const privateKey = fs.readFileSync(path); + const privateKey = fs.readFileSync(path); - return privateKey.toString("utf8"); + return privateKey.toString('utf8'); } export function decryptSessionKey( - encryptedSessionKey: string, - privateKey: string, + encryptedSessionKey: string, + privateKey: string, ): string { - const sessionKeyStructure = crypto.privateDecrypt( - privateKey, - Buffer.from(encryptedSessionKey, "hex"), - ); + const sessionKeyStructure = crypto.privateDecrypt( + privateKey, + Buffer.from(encryptedSessionKey, 'hex'), + ); - return sessionKeyStructure.toString("hex"); + return sessionKeyStructure.toString('hex'); } export function unpackUserLoginMessage(message: ISerializable): { - sessionKey: string; - gameId: string; - contextToken: string; + sessionKey: string; + gameId: string; + contextToken: string; } { - defaultLogger.debug("unpackUserLoginMessage called"); - defaultLogger.info(`Unpacking user login message: ${getAsHex(message.serialize())}`); + defaultLogger.debug('unpackUserLoginMessage called'); + defaultLogger.info( + `Unpacking user login message: ${getAsHex(message.serialize())}`, + ); - // Get the context token - const ticket = getLenString(message.serialize(), 0, false); + // Get the context token + const ticket = getLenString(message.serialize(), 0, false); - let dataOffset = ticket.length + 2; + let dataOffset = ticket.length + 2; - // The next data structure is a container with an empty id, a length, and a data structure + // The next data structure is a container with an empty id, a length, and a data structure - // Skip the empty id - dataOffset += 2; + // Skip the empty id + dataOffset += 2; - // Get the next data length - const nextDataLength = message.serialize().readUInt16BE(dataOffset); + // Get the next data length + const nextDataLength = message.serialize().readUInt16BE(dataOffset); - // This value is the encrypted session key hex, stored as a string - const encryptedSessionKey = message - .serialize() - .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength) - .toString("utf8"); + // This value is the encrypted session key hex, stored as a string + const encryptedSessionKey = message + .serialize() + .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength) + .toString('utf8'); - // Load the private key - const privateKey = loadPrivateKey(getServerConfiguration().privateKeyFile); + // Load the private key + const privateKey = loadPrivateKey(getServerConfiguration().privateKeyFile); - // Decrypt the session key - const sessionKey = decryptSessionKey(encryptedSessionKey, privateKey); + // Decrypt the session key + const sessionKey = decryptSessionKey(encryptedSessionKey, privateKey); - defaultLogger.info( - `Decrypted session key: ${getAsHex(Buffer.from(sessionKey, "hex"))}`, - ); + defaultLogger.info( + `Decrypted session key: ${getAsHex(Buffer.from(sessionKey, 'hex'))}`, + ); - // Unpack the session key - const sessionKeyStructure = SessionKey.fromBytes( - Buffer.from(sessionKey, "hex"), - ); + // Unpack the session key + const sessionKeyStructure = SessionKey.fromBytes( + Buffer.from(sessionKey, 'hex'), + ); - defaultLogger.info(`Session key structure: ${sessionKeyStructure.toString()}`); + defaultLogger.info( + `Session key structure: ${sessionKeyStructure.toString()}`, + ); - // Update the data offset - dataOffset += 2 + nextDataLength; + // Update the data offset + dataOffset += 2 + nextDataLength; - // Get the next data length - const nextDataLength2 = message.serialize().readUInt16BE(dataOffset); + // Get the next data length + const nextDataLength2 = message.serialize().readUInt16BE(dataOffset); - // This value is the game id (used by server to identify the game) - const gameId = message - .serialize() - .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength2) - .toString("utf8"); + // This value is the game id (used by server to identify the game) + const gameId = message + .serialize() + .subarray(dataOffset + 2, dataOffset + 2 + nextDataLength2) + .toString('utf8'); - // Update the data offset - dataOffset += 2 + nextDataLength2; + // Update the data offset + dataOffset += 2 + nextDataLength2; - // Return the session key, game id, and context token - return { - sessionKey: sessionKeyStructure.getKey(), - gameId, - contextToken: ticket, - }; + // Return the session key, game id, and context token + return { + sessionKey: sessionKeyStructure.getKey(), + gameId, + contextToken: ticket, + }; } /** * This is the initial connection to the Login server */ export async function processGameLogin( - _connectionId: string, - userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - Sentry.startSpan( - { - name: "processLogin", - op: "processLogin", - }, - () => { - defaultLogger.debug("processGameLogin called"); - - defaultLogger.info(`Login: ${message.toString()}`); - - // Unpack the message - try { - const { sessionKey, contextToken } = unpackUserLoginMessage( - message.getData(), - ); - - // Log the context token - defaultLogger.info(`Context token: ${contextToken}`); - - // Log the session key - defaultLogger.info(`Session key: ${sessionKey}`); - - // Look up the customer id - const user = getToken(contextToken); - - // If the user is not found, return an error - if (user === undefined) { - defaultLogger.error(`User not found for context token: ${contextToken}`); - - // Create a new message - Not found - const response = new GameMessage(0); - response.header.setId(0x602); - - // Send the message - twice - Sentry.startSpan( - { - name: "socketCallback", - op: "socketCallback", - }, - () => { - socketCallback([response.serialize()]); - }, - ); - Sentry.startSpan( - { - name: "socketCallback", - op: "socketCallback", - }, - () => { - socketCallback([response.serialize()]); - }, - ); - - return; - } - - // Log the user - defaultLogger.info(`User: ${user.customerId}`); - - // Create a new message - Login ACK - const loginACK = new GameMessage(0); - loginACK.header.setId(0x601); - - // Send the ack - socketCallback([loginACK.serialize()]); - - // Update the user status - userStatus.setCustomerId(user.customerId); - userStatus.setPersonaId(0); - userStatus.ban.set({ - initiator: "Molly", - startComment: "Because I said so", - }); - userStatus.setSessionKey(SessionKey.fromKeyString(sessionKey)); - - UserStatusManager.addUserStatus(userStatus); - // Create a new message - UserStatus - const userStatusMessage = new GameMessage(257); - userStatusMessage.header.setId(0x601); - - userStatusMessage.setData(userStatus); - - // Log the message - defaultLogger.info(`UserStatus: ${userStatusMessage.toString()}`); - - // Send the message - socketCallback([userStatusMessage.serialize()]); - socketCallback([userStatusMessage.serialize()]); - - return; - } catch (e) { - console.error(e); - } - }, - ); - return Promise.resolve(); + Sentry.startSpan( + { + name: 'processLogin', + op: 'processLogin', + }, + () => { + defaultLogger.debug('processGameLogin called'); + + defaultLogger.info(`Login: ${message.toString()}`); + + // Unpack the message + try { + const { sessionKey, contextToken } = unpackUserLoginMessage( + message.getData(), + ); + + // Log the context token + defaultLogger.info(`Context token: ${contextToken}`); + + // Log the session key + defaultLogger.info(`Session key: ${sessionKey}`); + + // Look up the customer id + const user = getToken(contextToken); + + // If the user is not found, return an error + if (user === undefined) { + defaultLogger.error( + `User not found for context token: ${contextToken}`, + ); + + // Create a new message - Not found + const response = new GameMessage(0); + response.header.setId(0x602); + + // Send the message - twice + Sentry.startSpan( + { + name: 'socketCallback', + op: 'socketCallback', + }, + () => { + socketCallback([response.serialize()]); + }, + ); + Sentry.startSpan( + { + name: 'socketCallback', + op: 'socketCallback', + }, + () => { + socketCallback([response.serialize()]); + }, + ); + + return; + } + + // Log the user + defaultLogger.info(`User: ${user.customerId}`); + + // Create a new message - Login ACK + const loginACK = new GameMessage(0); + loginACK.header.setId(0x601); + + // Send the ack + socketCallback([loginACK.serialize()]); + + // Update the user status + userStatus.setCustomerId(user.customerId); + userStatus.setPersonaId(0); + userStatus.ban.set({ + initiator: 'Molly', + startComment: 'Because I said so', + }); + userStatus.setSessionKey(SessionKey.fromKeyString(sessionKey)); + + UserStatusManager.addUserStatus(userStatus); + // Create a new message - UserStatus + const userStatusMessage = new GameMessage(257); + userStatusMessage.header.setId(0x601); + + userStatusMessage.setData(userStatus); + + // Log the message + defaultLogger.info( + `UserStatus: ${userStatusMessage.toString()}`, + ); + + // Send the message + socketCallback([userStatusMessage.serialize()]); + socketCallback([userStatusMessage.serialize()]); + + return; + } catch (e) { + console.error(e); + } + }, + ); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processGetFirstBuddy.ts b/packages/nps/gameMessageProcessors/processGetFirstBuddy.ts index 59a08aea2..4b74d4be4 100644 --- a/packages/nps/gameMessageProcessors/processGetFirstBuddy.ts +++ b/packages/nps/gameMessageProcessors/processGetFirstBuddy.ts @@ -1,43 +1,43 @@ import { - GameMessage, - // ProfileList, - SerializableData, - getDWord, - // getGameProfilesForCustomerId, -} from "rusty-motors-nps"; -import type { GameSocketCallback } from "./index.js"; + GameMessage, + // ProfileList, + SerializableData, + getDWord, + // getGameProfilesForCustomerId, +} from 'rusty-motors-nps'; +import type { GameSocketCallback } from './index.js'; -import type { UserStatus } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processFirstBuddy"); +const defaultLogger = getServerLogger('nps.processFirstBuddy'); export async function processFirstBuddy( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.info("processFirstBuddy called"); - const profileId = getDWord(message.getDataAsBuffer(), 0, false); + defaultLogger.info('processFirstBuddy called'); + const profileId = getDWord(message.getDataAsBuffer(), 0, false); - defaultLogger.info(`GetFirstBuddy profile: ${profileId}`); + defaultLogger.info(`GetFirstBuddy profile: ${profileId}`); - // TO: Look up the profiles for the customer ID - // const _profiles = getGameProfilesForCustomerId(profileId); + // TO: Look up the profiles for the customer ID + // const _profiles = getGameProfilesForCustomerId(profileId); - // TO: Create a new NPSList of profiles - // const _list = new ProfileList(); + // TO: Create a new NPSList of profiles + // const _list = new ProfileList(); - const outMessage = new GameMessage(257); - outMessage.header.setId(0x614); - outMessage.setData(new SerializableData(4)); + const outMessage = new GameMessage(257); + outMessage.header.setId(0x614); + outMessage.setData(new SerializableData(4)); - // Log the message - defaultLogger.info(`GetFirstBuddy: ${outMessage.toString()}`); + // Log the message + defaultLogger.info(`GetFirstBuddy: ${outMessage.toString()}`); - defaultLogger.info("==========================================="); + defaultLogger.info('==========================================='); - socketCallback([outMessage.serialize()]); - return Promise.resolve(); + socketCallback([outMessage.serialize()]); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processGetProfileInfo.ts b/packages/nps/gameMessageProcessors/processGetProfileInfo.ts index 7f475d27f..97a89411e 100644 --- a/packages/nps/gameMessageProcessors/processGetProfileInfo.ts +++ b/packages/nps/gameMessageProcessors/processGetProfileInfo.ts @@ -1,64 +1,64 @@ import { - GameMessage, - ProfileList, - getAsHex, - getDWord, - getGameProfilesForCustomerId, -} from "rusty-motors-nps"; -import type { GameSocketCallback } from "./index.js"; + GameMessage, + ProfileList, + getAsHex, + getDWord, + getGameProfilesForCustomerId, +} from 'rusty-motors-nps'; +import type { GameSocketCallback } from './index.js'; -import type { UserStatus } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processGetProfileInfo"); +const defaultLogger = getServerLogger('nps.processGetProfileInfo'); export async function processGetProfileInfo( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - const customerId = getDWord(message.serialize(), 0, false); + const customerId = getDWord(message.serialize(), 0, false); - defaultLogger.info(`GetProfileInfo: ${customerId}`); + defaultLogger.info(`GetProfileInfo: ${customerId}`); - // Look up the profiles for the customer ID - const profiles = getGameProfilesForCustomerId(customerId); + // Look up the profiles for the customer ID + const profiles = getGameProfilesForCustomerId(customerId); - // Create a new NPSList of profiles - const list = new ProfileList(); + // Create a new NPSList of profiles + const list = new ProfileList(); - const outMessage = new GameMessage(0); + const outMessage = new GameMessage(0); - // Add each profile to the list - if (profiles) { - outMessage.header.setId(0x607); - for (const profile of profiles) { - // Log the profile - defaultLogger.info(`GetProfileInfo: ${profile.toString()}`); // TODO: Remove this line + // Add each profile to the list + if (profiles) { + outMessage.header.setId(0x607); + for (const profile of profiles) { + // Log the profile + defaultLogger.info(`GetProfileInfo: ${profile.toString()}`); // TODO: Remove this line - list.addProfile(profile); - } - } else { - outMessage.header.setId(0x602); - } + list.addProfile(profile); + } + } else { + outMessage.header.setId(0x602); + } - // Send the list back to the client - try { - // Log the message data - defaultLogger.info(`GetProfileInfo: ${getAsHex(list.serialize())}`); + // Send the list back to the client + try { + // Log the message data + defaultLogger.info(`GetProfileInfo: ${getAsHex(list.serialize())}`); - outMessage.setData(list); + outMessage.setData(list); - // Log the message - defaultLogger.info(`GetProfileInfo: ${outMessage.toString()}`); + // Log the message + defaultLogger.info(`GetProfileInfo: ${outMessage.toString()}`); - defaultLogger.info("==========================================="); + defaultLogger.info('==========================================='); - socketCallback([outMessage.serialize()]); - return Promise.resolve(); - } catch (error) { - defaultLogger.error(`Error sending profile info: ${error as string}`); - throw new Error("Error sending profile info"); - } + socketCallback([outMessage.serialize()]); + return Promise.resolve(); + } catch (error) { + defaultLogger.error(`Error sending profile info: ${error as string}`); + throw new Error('Error sending profile info'); + } } diff --git a/packages/nps/gameMessageProcessors/processGetProfileMaps.ts b/packages/nps/gameMessageProcessors/processGetProfileMaps.ts index a6d981c14..01462183d 100644 --- a/packages/nps/gameMessageProcessors/processGetProfileMaps.ts +++ b/packages/nps/gameMessageProcessors/processGetProfileMaps.ts @@ -1,67 +1,69 @@ import { - GameMessage, - ProfileList, - getAsHex, - getDWord, - getGameProfilesForCustomerId, -} from "rusty-motors-nps"; -import type { GameSocketCallback } from "./index.js"; -import type { UserStatus } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; + GameMessage, + ProfileList, + getAsHex, + getDWord, + getGameProfilesForCustomerId, +} from 'rusty-motors-nps'; +import type { GameSocketCallback } from './index.js'; +import type { UserStatus } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; export async function processGetProfileMaps( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - const defaultLogger = getServerLogger("nps.processGetProfileMaps"); - // This message is a version 257, but it's version is set to 0 - // This is a bug in the client, so we need to generate a new message - // with the correct version - const requestMessage = GameMessage.fromGameMessage(257, message); + const defaultLogger = getServerLogger('nps.processGetProfileMaps'); + // This message is a version 257, but it's version is set to 0 + // This is a bug in the client, so we need to generate a new message + // with the correct version + const requestMessage = GameMessage.fromGameMessage(257, message); - defaultLogger.info(`GetProfileMaps (257): ${requestMessage.toString()}`); + defaultLogger.info(`GetProfileMaps (257): ${requestMessage.toString()}`); - const customerId = getDWord(requestMessage.getDataAsBuffer(), 0, false); + const customerId = getDWord(requestMessage.getDataAsBuffer(), 0, false); - defaultLogger.info(`GetProfileMaps: ${customerId}`); + defaultLogger.info(`GetProfileMaps: ${customerId}`); - // Look up the profiles for the customer ID - const profiles = getGameProfilesForCustomerId(customerId); + // Look up the profiles for the customer ID + const profiles = getGameProfilesForCustomerId(customerId); - // Create a new NPSList of profiles - const list = new ProfileList(); + // Create a new NPSList of profiles + const list = new ProfileList(); - // Add each profile to the list - if (profiles) { - for (const profile of profiles) { - // Log the profile - defaultLogger.info(`GetProfileMaps: ${profile.toString()}`); + // Add each profile to the list + if (profiles) { + for (const profile of profiles) { + // Log the profile + defaultLogger.info(`GetProfileMaps: ${profile.toString()}`); - list.addProfile(profile); - } - } + list.addProfile(profile); + } + } - // Send the list back to the client - try { - const outMessage = new GameMessage(257); - outMessage.header.setId(0x607); + // Send the list back to the client + try { + const outMessage = new GameMessage(257); + outMessage.header.setId(0x607); - // Log the message data - defaultLogger.info(`GetProfileMaps: ${getAsHex(outMessage.serialize())}`); + // Log the message data + defaultLogger.info( + `GetProfileMaps: ${getAsHex(outMessage.serialize())}`, + ); - outMessage.setData(list); + outMessage.setData(list); - // Log the message - defaultLogger.info(`GetProfileMaps: ${outMessage.toString()}`); + // Log the message + defaultLogger.info(`GetProfileMaps: ${outMessage.toString()}`); - defaultLogger.info("==========================================="); + defaultLogger.info('==========================================='); - socketCallback([outMessage.serialize()]); - return Promise.resolve(); - } catch (error) { - defaultLogger.error(`Error sending profile info: ${error as string}`); - throw new Error("Error sending profile info"); - } + socketCallback([outMessage.serialize()]); + return Promise.resolve(); + } catch (error) { + defaultLogger.error(`Error sending profile info: ${error as string}`); + throw new Error('Error sending profile info'); + } } diff --git a/packages/nps/gameMessageProcessors/processPing.ts b/packages/nps/gameMessageProcessors/processPing.ts index fc01ef2a3..abc1e7766 100644 --- a/packages/nps/gameMessageProcessors/processPing.ts +++ b/packages/nps/gameMessageProcessors/processPing.ts @@ -1,20 +1,20 @@ -import { GameMessage } from "rusty-motors-nps"; -import type { GameSocketCallback } from "./index.js"; +import { GameMessage } from 'rusty-motors-nps'; +import type { GameSocketCallback } from './index.js'; -import type { UserStatus } from "rusty-motors-nps"; -import { sendNPSAck } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from 'rusty-motors-nps'; +import { sendNPSAck } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processPing"); +const defaultLogger = getServerLogger('nps.processPing'); export async function processPing( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.info(`Ping: ${message.toString()}`); + defaultLogger.info(`Ping: ${message.toString()}`); - sendNPSAck(socketCallback); - return Promise.resolve(); + sendNPSAck(socketCallback); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processSelectPersona.ts b/packages/nps/gameMessageProcessors/processSelectPersona.ts index a6aa5ecbe..67de76e34 100644 --- a/packages/nps/gameMessageProcessors/processSelectPersona.ts +++ b/packages/nps/gameMessageProcessors/processSelectPersona.ts @@ -1,47 +1,49 @@ -import { GameMessage, getDWord } from "rusty-motors-nps"; -import type { GameSocketCallback } from "./index.js"; +import { GameMessage, getDWord } from 'rusty-motors-nps'; +import type { GameSocketCallback } from './index.js'; -import { UserStatus, UserStatusManager, sendNPSAck } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; +import { UserStatus, UserStatusManager, sendNPSAck } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processSelectPersona"); +const defaultLogger = getServerLogger('nps.processSelectPersona'); export async function processSelectPersona( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.info(`SelectPersona: ${message.toString()}`); + defaultLogger.info(`SelectPersona: ${message.toString()}`); - const customerId = getDWord(message.getDataAsBuffer(), 0, false); + const customerId = getDWord(message.getDataAsBuffer(), 0, false); - const personaId = getDWord(message.getDataAsBuffer(), 4, false); + const personaId = getDWord(message.getDataAsBuffer(), 4, false); - const shardId = getDWord(message.getDataAsBuffer(), 8, false); + const shardId = getDWord(message.getDataAsBuffer(), 8, false); - // Log the values - defaultLogger.info(`Customer ID: ${customerId}`); - defaultLogger.info(`Persona ID: ${personaId}`); - defaultLogger.info(`Shard ID: ${shardId}`); + // Log the values + defaultLogger.info(`Customer ID: ${customerId}`); + defaultLogger.info(`Persona ID: ${personaId}`); + defaultLogger.info(`Shard ID: ${shardId}`); - // Lookup the session - const existingStatus = UserStatusManager.getUserStatus(customerId); + // Lookup the session + const existingStatus = UserStatusManager.getUserStatus(customerId); - if (!existingStatus) { - defaultLogger.error(`UserStatus not found for customer ID ${customerId}`); - throw new Error(`UserStatus not found for customer ID ${customerId}`); - } + if (!existingStatus) { + defaultLogger.error( + `UserStatus not found for customer ID ${customerId}`, + ); + throw new Error(`UserStatus not found for customer ID ${customerId}`); + } - defaultLogger.info( - `Setting persona ID to ${personaId} for ${existingStatus.getCustomerId()}`, - ); + defaultLogger.info( + `Setting persona ID to ${personaId} for ${existingStatus.getCustomerId()}`, + ); - // Update the user status - existingStatus.setPersonaId(personaId); + // Update the user status + existingStatus.setPersonaId(personaId); - defaultLogger.info(`GameLogin: ${message.toString()}`); + defaultLogger.info(`GameLogin: ${message.toString()}`); - sendNPSAck(socketCallback); - return Promise.resolve(); + sendNPSAck(socketCallback); + return Promise.resolve(); } diff --git a/packages/nps/gameMessageProcessors/processUserLogin.ts b/packages/nps/gameMessageProcessors/processUserLogin.ts index cba4342f1..d35be4931 100644 --- a/packages/nps/gameMessageProcessors/processUserLogin.ts +++ b/packages/nps/gameMessageProcessors/processUserLogin.ts @@ -1,67 +1,69 @@ import { - GameMessage, - UserInfo, - getDWord, - getLenString, -} from "rusty-motors-nps"; -import type { GameSocketCallback } from "./index.js"; + GameMessage, + UserInfo, + getDWord, + getLenString, +} from 'rusty-motors-nps'; +import type { GameSocketCallback } from './index.js'; -import type { UserStatus } from "rusty-motors-nps"; -import { UserStatusManager, getCustomerId } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; +import type { UserStatus } from 'rusty-motors-nps'; +import { UserStatusManager, getCustomerId } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.processUserLogin"); +const defaultLogger = getServerLogger('nps.processUserLogin'); export async function processUserLogin( - _connectionId: string, - _userStatus: UserStatus, - message: GameMessage, - socketCallback: GameSocketCallback, + _connectionId: string, + _userStatus: UserStatus, + message: GameMessage, + socketCallback: GameSocketCallback, ): Promise { - defaultLogger.info(`UserLogin: ${message.toString()}`); + defaultLogger.info(`UserLogin: ${message.toString()}`); - // This message is a BareMessageV0 + // This message is a BareMessageV0 - const personaId = getDWord(message.getDataAsBuffer(), 0, false); + const personaId = getDWord(message.getDataAsBuffer(), 0, false); - const profileName = getLenString(message.getDataAsBuffer(), 4, false); + const profileName = getLenString(message.getDataAsBuffer(), 4, false); - // Lookup customerID from personaID - const customerID = getCustomerId(personaId); + // Lookup customerID from personaID + const customerID = getCustomerId(personaId); - if (customerID === -1) { - defaultLogger.error(`CustomerID not found for personaID: ${personaId}`); - throw new Error(`CustomerID not found for personaID: ${personaId}`); - } + if (customerID === -1) { + defaultLogger.error(`CustomerID not found for personaID: ${personaId}`); + throw new Error(`CustomerID not found for personaID: ${personaId}`); + } - defaultLogger.info(`LobbyLogin: ${personaId} ${profileName} ${customerID}`); + defaultLogger.info(`LobbyLogin: ${personaId} ${profileName} ${customerID}`); - const existingStatus = UserStatusManager.getUserStatus(customerID); + const existingStatus = UserStatusManager.getUserStatus(customerID); - if (typeof existingStatus === "undefined") { - defaultLogger.error(`UserStatus not found for customerID: ${customerID}`); - throw new Error(`UserStatus not found for customerID: ${customerID}`); - } + if (typeof existingStatus === 'undefined') { + defaultLogger.error( + `UserStatus not found for customerID: ${customerID}`, + ); + throw new Error(`UserStatus not found for customerID: ${customerID}`); + } - // Update the user status - existingStatus.setPersonaId(personaId); + // Update the user status + existingStatus.setPersonaId(personaId); - _userStatus = existingStatus; + _userStatus = existingStatus; - defaultLogger.info(`LobbyLogin: ${message.toString()}`); + defaultLogger.info(`LobbyLogin: ${message.toString()}`); - const response = new UserInfo(personaId, profileName); + const response = new UserInfo(personaId, profileName); - defaultLogger.info(`Sending response: ${response.toString()}`); + defaultLogger.info(`Sending response: ${response.toString()}`); - const responseMessage = new GameMessage(0); - responseMessage.header.setId(0x120); + const responseMessage = new GameMessage(0); + responseMessage.header.setId(0x120); - responseMessage.setData(response); + responseMessage.setData(response); - defaultLogger.info(`Response message: ${responseMessage.toString()}`); + defaultLogger.info(`Response message: ${responseMessage.toString()}`); - const responseBytes = responseMessage.serialize(); + const responseBytes = responseMessage.serialize(); - socketCallback([responseBytes]); + socketCallback([responseBytes]); } diff --git a/packages/nps/messageStructs/BaseSerializable.ts b/packages/nps/messageStructs/BaseSerializable.ts index 8f99d762f..b8928a3d0 100644 --- a/packages/nps/messageStructs/BaseSerializable.ts +++ b/packages/nps/messageStructs/BaseSerializable.ts @@ -1,16 +1,16 @@ -import type { ISerializable } from "../types"; +import type { ISerializable } from '../types.js'; export class BaseSerializable implements ISerializable { - serialize(): Buffer { - throw new Error("Method not implemented."); - } - deserialize(_data: Buffer): void { - throw new Error("Method not implemented."); - } - getByteSize(): number { - throw new Error("Method not implemented."); - } - toString(): string { - throw new Error("Method not implemented."); - } + serialize(): Buffer { + throw new Error('Method not implemented.'); + } + deserialize(_data: Buffer): void { + throw new Error('Method not implemented.'); + } + getByteSize(): number { + throw new Error('Method not implemented.'); + } + toString(): string { + throw new Error('Method not implemented.'); + } } diff --git a/packages/nps/messageStructs/MiniRiffList.ts b/packages/nps/messageStructs/MiniRiffList.ts index b3c43ab23..60d2615ed 100644 --- a/packages/nps/messageStructs/MiniRiffList.ts +++ b/packages/nps/messageStructs/MiniRiffList.ts @@ -1,92 +1,95 @@ -import { putLenString } from "rusty-motors-nps"; +import { putLenString } from 'rusty-motors-nps'; -import { BaseSerializable } from "./BaseSerializable.js"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("nps.MiniRiffList"); +import { BaseSerializable } from './BaseSerializable.js'; +import { getServerLogger } from 'rusty-motors-logger'; +const defaultLogger = getServerLogger('nps.MiniRiffList'); // const channelRecordSize = 40; export class MiniRiffInfo extends BaseSerializable { - riffName: string; // 32 bytes - max length - riffId: number; // 4 bytes - population: number; // 2 bytes + riffName: string; // 32 bytes - max length + riffId: number; // 4 bytes + population: number; // 2 bytes - constructor(riffName: string, riffId: number, population: number) { - super(); - if (riffName.length > 32) { - throw new Error(`Riff name too long: ${riffName}`); - } + constructor(riffName: string, riffId: number, population: number) { + super(); + if (riffName.length > 32) { + throw new Error(`Riff name too long: ${riffName}`); + } - this.riffName = riffName; - this.riffId = riffId; - this.population = population; - } + this.riffName = riffName; + this.riffId = riffId; + this.population = population; + } - override serialize(): Buffer { - const buffer = Buffer.alloc(this.getByteSize()); - let offset = 0; - putLenString(buffer, offset, this.riffName, false); - offset += 2 + this.riffName.length + 1; - buffer.writeUInt32BE(this.riffId, offset); - offset += 4; - buffer.writeUInt16BE(this.population, offset); - defaultLogger.debug(`MiniRiffInfo: ${this.toString()} - ${buffer.toString("hex")}`); - return buffer; - } - override getByteSize(): number { - return 4 + this.riffName.length + 1 + 4 + 2; - } - override toString(): string { - return `MiniRiffInfo(riffName=${this.riffName}, riffId=${this.riffId}, population=${this.population})`; - } + override serialize(): Buffer { + const buffer = Buffer.alloc(this.getByteSize()); + let offset = 0; + putLenString(buffer, offset, this.riffName, false); + offset += 2 + this.riffName.length + 1; + buffer.writeUInt32BE(this.riffId, offset); + offset += 4; + buffer.writeUInt16BE(this.population, offset); + defaultLogger.debug( + `MiniRiffInfo: ${this.toString()} - ${buffer.toString('hex')}`, + ); + return buffer; + } + override getByteSize(): number { + return 4 + this.riffName.length + 1 + 4 + 2; + } + override toString(): string { + return `MiniRiffInfo(riffName=${this.riffName}, riffId=${this.riffId}, population=${this.population})`; + } } export class MiniRiffList extends BaseSerializable { - private riffs: MiniRiffInfo[] = []; + private riffs: MiniRiffInfo[] = []; - override serialize(): Buffer { - return this.toBytes(); - } - override getByteSize(): number { - return this.getSize(); - } + override serialize(): Buffer { + return this.toBytes(); + } + override getByteSize(): number { + return this.getSize(); + } - getMaxRiffs(): number { - return this.riffs.length; - } + getMaxRiffs(): number { + return this.riffs.length; + } - addRiff(riff: MiniRiffInfo): void { - this.riffs.push(riff); - } + addRiff(riff: MiniRiffInfo): void { + this.riffs.push(riff); + } - toBytes(): Buffer { - const buffer = Buffer.alloc(this.getSize()); - let offset = 0; - buffer.writeUInt32BE(this.riffs.length, offset); - offset += 4; - for (const riff of this.riffs) { - const riffBuffer = riff.serialize(); - riffBuffer.copy(buffer, offset); - offset += riff.getByteSize(); - } + toBytes(): Buffer { + const buffer = Buffer.alloc(this.getSize()); + let offset = 0; + buffer.writeUInt32BE(this.riffs.length, offset); + offset += 4; + for (const riff of this.riffs) { + const riffBuffer = riff.serialize(); + riffBuffer.copy(buffer, offset); + offset += riff.getByteSize(); + } - defaultLogger.debug(`MiniRiffList: ${this.toString()} - ${buffer.toString("hex")}`); - return buffer; - } - override toString(): string { - return `MiniRiffList(riffs=${this.riffs})`; - } - toHex(): string { - return this.toBytes().toString("hex"); - } + defaultLogger.debug( + `MiniRiffList: ${this.toString()} - ${buffer.toString('hex')}`, + ); + return buffer; + } + override toString(): string { + return `MiniRiffList(riffs=${this.riffs})`; + } + toHex(): string { + return this.toBytes().toString('hex'); + } - getSize(): number { - let size = 4; - for (const riff of this.riffs) { - size += riff.getByteSize(); - } - return size; - } + getSize(): number { + let size = 4; + for (const riff of this.riffs) { + size += riff.getByteSize(); + } + return size; + } } diff --git a/packages/nps/messageStructs/SessionKey.ts b/packages/nps/messageStructs/SessionKey.ts index 807ec76e6..f42c01302 100644 --- a/packages/nps/messageStructs/SessionKey.ts +++ b/packages/nps/messageStructs/SessionKey.ts @@ -1,108 +1,110 @@ -import { getAsHex, isOnlyOneSet } from "rusty-motors-nps"; -import { BaseSerializable } from "./BaseSerializable.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { getAsHex, isOnlyOneSet } from 'rusty-motors-nps'; +import { BaseSerializable } from './BaseSerializable.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("nps.SessionKey"); +const defaultLogger = getServerLogger('nps.SessionKey'); export class SessionKey extends BaseSerializable { - private key: Buffer = Buffer.alloc(0); - private timestamp: number = 0; - private _isSet: boolean = false; - - constructor({ key, timestamp }: { key?: Buffer; timestamp?: number }) { - super(); - if (isOnlyOneSet(key, timestamp)) { - throw new Error("Both key and timestamp must be set if one is set"); - } - - if (typeof key !== "undefined" && typeof timestamp !== "undefined") { - defaultLogger.debug(`SessionKey: key=${getAsHex(key)}, timestamp=${timestamp}`); - this.key = key; - this.timestamp = timestamp; - this._isSet = true; - } - } - override serialize(): Buffer { - return this.toBytes(); - } - override deserialize(data: Buffer): void { - SessionKey.fromBytes(data); - } - override getByteSize(): number { - throw new Error("Method not implemented."); - } - - static fromBytes(bytes: Buffer): SessionKey { - defaultLogger.debug("SessionKey.fromBytes"); - const keyLength = bytes.readUInt16BE(0); - - // Set the data offset - const dataOffset = 2 + keyLength; - - const key = bytes.subarray(2, dataOffset); - - defaultLogger.debug(`SessionKey.fromBytes: key=${getAsHex(key)}`); - - // Get the timestamp - const timestamp = bytes.readUInt32BE(dataOffset); - - return new SessionKey({ - key, - timestamp, - }); - } - - static fromKeyString(key: string): SessionKey { - const keyBuffer = Buffer.from(key, "hex"); - - return new SessionKey({ - key: keyBuffer, - timestamp: 0, - }); - } - - getKey(): string { - return this.key.toString("hex"); - } - - override toString(): string { - return `SessionKey(key=${this.getKey()}, timestamp=${this.timestamp})`; - } - - toHex(): string { - return getAsHex(this.toBytes()); - } - - toBytes(): Buffer { - if (!this.isSet()) { - throw new Error("Session key is not set"); - } - - const keyLength = this.key.length; - const timestamp = this.timestamp; - - const buffer = Buffer.alloc(2 + keyLength + 4); - - buffer.writeUInt16BE(keyLength, 0); - this.key.copy(buffer, 2); - buffer.writeUInt32BE(timestamp, 2 + keyLength); - - return buffer; - } - - getSize(): number { - return this.key.length + 6; - } - - getData(): Buffer { - throw new Error("Method not implemented."); - } - - setData(): void { - throw new Error("Method not implemented."); - } - - isSet(): boolean { - return this._isSet; - } + private key: Buffer = Buffer.alloc(0); + private timestamp = 0; + private _isSet = false; + + constructor({ key, timestamp }: { key?: Buffer; timestamp?: number }) { + super(); + if (isOnlyOneSet(key, timestamp)) { + throw new Error('Both key and timestamp must be set if one is set'); + } + + if (typeof key !== 'undefined' && typeof timestamp !== 'undefined') { + defaultLogger.debug( + `SessionKey: key=${getAsHex(key)}, timestamp=${timestamp}`, + ); + this.key = key; + this.timestamp = timestamp; + this._isSet = true; + } + } + override serialize(): Buffer { + return this.toBytes(); + } + override deserialize(data: Buffer): void { + SessionKey.fromBytes(data); + } + override getByteSize(): number { + throw new Error('Method not implemented.'); + } + + static fromBytes(bytes: Buffer): SessionKey { + defaultLogger.debug('SessionKey.fromBytes'); + const keyLength = bytes.readUInt16BE(0); + + // Set the data offset + const dataOffset = 2 + keyLength; + + const key = bytes.subarray(2, dataOffset); + + defaultLogger.debug(`SessionKey.fromBytes: key=${getAsHex(key)}`); + + // Get the timestamp + const timestamp = bytes.readUInt32BE(dataOffset); + + return new SessionKey({ + key, + timestamp, + }); + } + + static fromKeyString(key: string): SessionKey { + const keyBuffer = Buffer.from(key, 'hex'); + + return new SessionKey({ + key: keyBuffer, + timestamp: 0, + }); + } + + getKey(): string { + return this.key.toString('hex'); + } + + override toString(): string { + return `SessionKey(key=${this.getKey()}, timestamp=${this.timestamp})`; + } + + toHex(): string { + return getAsHex(this.toBytes()); + } + + toBytes(): Buffer { + if (!this.isSet()) { + throw new Error('Session key is not set'); + } + + const keyLength = this.key.length; + const timestamp = this.timestamp; + + const buffer = Buffer.alloc(2 + keyLength + 4); + + buffer.writeUInt16BE(keyLength, 0); + this.key.copy(buffer, 2); + buffer.writeUInt32BE(timestamp, 2 + keyLength); + + return buffer; + } + + getSize(): number { + return this.key.length + 6; + } + + getData(): Buffer { + throw new Error('Method not implemented.'); + } + + setData(): void { + throw new Error('Method not implemented.'); + } + + isSet(): boolean { + return this._isSet; + } } diff --git a/packages/nps/messageStructs/UserAction.ts b/packages/nps/messageStructs/UserAction.ts index dbf60e3d5..f7c5699bc 100644 --- a/packages/nps/messageStructs/UserAction.ts +++ b/packages/nps/messageStructs/UserAction.ts @@ -1,139 +1,142 @@ -import type { ISerializable } from "rusty-motors-nps"; -import { getAsHex } from "rusty-motors-nps"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("nps.UserAction"); +import type { ISerializable } from 'rusty-motors-nps'; +import { getAsHex } from 'rusty-motors-nps'; +import { getServerLogger } from 'rusty-motors-logger'; +const defaultLogger = getServerLogger('nps.UserAction'); export class UserAction implements ISerializable { - private name: string; - private _endTimeMaybe = 0; // 4 bytes - private _b1 = 0; // 1 byte - private _b2 = 0; // 1 byte - private _initiator = ""; // 64 bytes - private _startComment = ""; // 256 bytes - private _endComment = ""; // 256 bytes + private name: string; + private _endTimeMaybe = 0; // 4 bytes + private _b1 = 0; // 1 byte + private _b2 = 0; // 1 byte + private _initiator = ''; // 64 bytes + private _startComment = ''; // 256 bytes + private _endComment = ''; // 256 bytes - constructor(name: string) { - this.name = name; - } + constructor(name: string) { + this.name = name; + } - serialize(): Buffer { - try { - const buffer = Buffer.alloc(this.getSize()); - let offset = 0; - buffer.writeUInt32BE(this._endTimeMaybe, offset); - offset += 4; - buffer.writeUInt8(this._b1, offset); - offset += 1; - buffer.writeUInt8(this._b2, offset); - offset += 1; - buffer.write(this._initiator, offset, 0x40, "utf8"); - offset += 0x40; - buffer.write(this._startComment, offset, 0x100, "utf8"); - offset += 0x100; - buffer.write(this._endComment, offset, 0x100, "utf8"); + serialize(): Buffer { + try { + const buffer = Buffer.alloc(this.getSize()); + let offset = 0; + buffer.writeUInt32BE(this._endTimeMaybe, offset); + offset += 4; + buffer.writeUInt8(this._b1, offset); + offset += 1; + buffer.writeUInt8(this._b2, offset); + offset += 1; + buffer.write(this._initiator, offset, 0x40, 'utf8'); + offset += 0x40; + buffer.write(this._startComment, offset, 0x100, 'utf8'); + offset += 0x100; + buffer.write(this._endComment, offset, 0x100, 'utf8'); - return buffer; - } catch (error: unknown) { - defaultLogger.error(`Error serializing UserAction: ${error as string}`); - throw error; - } - } - deserialize(data: Buffer): void { - try { - this._endTimeMaybe = data.readUInt32BE(0); - this._b1 = data.readUInt8(4); - this._b2 = data.readUInt8(5); - this._initiator = data.toString("utf8", 6, 0x46); - this._startComment = data.toString("utf8", 0x46, 0x146); - this._endComment = data.toString("utf8", 0x146, 0x246); - } catch (error) { - defaultLogger.error(`Error deserializing UserAction: ${error as string}`); - throw error; - } - } - getByteSize(): number { - throw new Error("Method not implemented."); - } - setData(): void { - throw new Error("Method not implemented."); - } - getData(): Buffer { - throw new Error("Method not implemented."); - } + return buffer; + } catch (error: unknown) { + defaultLogger.error( + `Error serializing UserAction: ${error as string}`, + ); + throw error; + } + } + deserialize(data: Buffer): void { + try { + this._endTimeMaybe = data.readUInt32BE(0); + this._b1 = data.readUInt8(4); + this._b2 = data.readUInt8(5); + this._initiator = data.toString('utf8', 6, 0x46); + this._startComment = data.toString('utf8', 0x46, 0x146); + this._endComment = data.toString('utf8', 0x146, 0x246); + } catch (error) { + defaultLogger.error( + `Error deserializing UserAction: ${error as string}`, + ); + throw error; + } + } + getByteSize(): number { + throw new Error('Method not implemented.'); + } + setData(): void { + throw new Error('Method not implemented.'); + } + getData(): Buffer { + throw new Error('Method not implemented.'); + } - static fromBytes(name: string, bytes: Buffer): UserAction { - const userAction = new UserAction(name); - userAction.deserialize(bytes); + static fromBytes(name: string, bytes: Buffer): UserAction { + const userAction = new UserAction(name); + userAction.deserialize(bytes); - return userAction; - } + return userAction; + } - toBytes(): Buffer { - return this.serialize(); - } - toString(): string { - return `UserAction: ${this.name} - ${this._initiator} - ${this._startComment} - ${this._endComment}`; - } - toHex(): string { - return getAsHex(this.serialize()); - } + toBytes(): Buffer { + return this.serialize(); + } + toString(): string { + return `UserAction: ${this.name} - ${this._initiator} - ${this._startComment} - ${this._endComment}`; + } + toHex(): string { + return getAsHex(this.serialize()); + } - getSize(): number { - return 4 + 1 + 1 + 0x40 + 0x100 + 0x100; - } + getSize(): number { + return 4 + 1 + 1 + 0x40 + 0x100 + 0x100; + } - public set({ - endTimeMaybe, - b1, - b2, - initiator, - startComment, - endComment, - }: { - endTimeMaybe?: number; - b1?: number; - b2?: number; - initiator: string; - startComment: string; - endComment?: string; - }): void { - this._endTimeMaybe = endTimeMaybe || 0; - this._b1 = b1 || 0; - this._b2 = b2 || 0; - this._initiator = initiator; - this._startComment = startComment; - this._endComment = endComment || ""; - } + public set({ + endTimeMaybe, + b1, + b2, + initiator, + startComment, + endComment, + }: { + endTimeMaybe?: number; + b1?: number; + b2?: number; + initiator: string; + startComment: string; + endComment?: string; + }): void { + this._endTimeMaybe = endTimeMaybe || 0; + this._b1 = b1 || 0; + this._b2 = b2 || 0; + this._initiator = initiator; + this._startComment = startComment; + this._endComment = endComment || ''; + } - public clear(): void { - this._endTimeMaybe = 0; - this._b1 = 0; - this._b2 = 0; - this._initiator = ""; - this._startComment = ""; - this._endComment = ""; - } + public clear(): void { + this._endTimeMaybe = 0; + this._b1 = 0; + this._b2 = 0; + this._initiator = ''; + this._startComment = ''; + this._endComment = ''; + } - updateEmptyValuesFrom(other: UserAction) { - if (this._endTimeMaybe === 0) { - this._endTimeMaybe = other._endTimeMaybe; - } - if (this._b1 === 0) { - this._b1 = other._b1; - } - if (this._b2 === 0) { - this._b2 = other._b2; - } - if (this._initiator === "") { - this._initiator = other._initiator; - } - if (this._startComment === "") { - this._startComment = other._startComment; - } - if (this._endComment === "") { - this._endComment = other._endComment; - } - } + updateEmptyValuesFrom(other: UserAction) { + if (this._endTimeMaybe === 0) { + this._endTimeMaybe = other._endTimeMaybe; + } + if (this._b1 === 0) { + this._b1 = other._b1; + } + if (this._b2 === 0) { + this._b2 = other._b2; + } + if (this._initiator === '') { + this._initiator = other._initiator; + } + if (this._startComment === '') { + this._startComment = other._startComment; + } + if (this._endComment === '') { + this._endComment = other._endComment; + } + } } diff --git a/packages/persona/index.ts b/packages/persona/index.ts index a7d969060..6d2a80298 100644 --- a/packages/persona/index.ts +++ b/packages/persona/index.ts @@ -1,2 +1,18 @@ -export { getPersonasByPersonaId } from "./src/getPersonasByPersonaId.js"; -export { receivePersonaData } from "./src/receivePersonaData.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export { getPersonasByPersonaId } from './src/getPersonasByPersonaId.js'; +export { receivePersonaData } from './src/receivePersonaData.js'; diff --git a/packages/persona/package.json b/packages/persona/package.json index 157a2ee64..264e6463a 100644 --- a/packages/persona/package.json +++ b/packages/persona/package.json @@ -19,6 +19,7 @@ "license": "AGPL-3.0", "dependencies": { "@sentry/profiling-node": "9.19.0", + "rusty-motors-logger": "workspace:^", "short-unique-id": "^5.3.2" }, "description": "", diff --git a/packages/persona/src/PersonaMapsMessage.test.ts b/packages/persona/src/PersonaMapsMessage.test.ts new file mode 100644 index 000000000..c53c8dc25 --- /dev/null +++ b/packages/persona/src/PersonaMapsMessage.test.ts @@ -0,0 +1,288 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { + PersonaMapsMessage, + PersonaList, + PersonaRecord, + serializeString, +} from './PersonaMapsMessage.js'; +import { BytableHeader } from '@rustymotors/binary'; + +describe('PersonaMapsMessage', () => { + let message: PersonaMapsMessage; + + beforeEach(() => { + message = new PersonaMapsMessage(); + // @ts-ignore: _header is protected + message._header = new BytableHeader(); + }); + + const defaultPersonaRecordSize = PersonaRecord.size; + beforeEach(() => { + // Always restore the original size method before each test + // @ts-ignore + PersonaRecord.size = defaultPersonaRecordSize; + }); + afterEach(() => { + // Always restore the original size method after each test + // @ts-ignore + PersonaRecord.size = defaultPersonaRecordSize; + }); + + it('should instantiate with undefined personaRecords', () => { + expect(message._personaRecords).toBeUndefined(); + }); + + it('should throw when serializing without personaRecords', () => { + expect(() => message.serialize()).toThrow( + 'PersonaRecords is undefined', + ); + }); + + it('should serialize and deserialize correctly with personaRecords', () => { + // Prepare PersonaList with one PersonaRecord + const personaList = new PersonaList(); + const personaRecord = new PersonaRecord(); + personaRecord.customerId = 123; + personaRecord.personaId = 456; + personaRecord.personaName = 'TestPersona'; + personaRecord.shardId = 789; + personaList.addPersonaRecord(personaRecord); + + message._personaRecords = personaList; + + // Set up header + // @ts-ignore: _header is protected + message._header.length = + message.header.serializeSize + 2 + personaList.size(); + + const buffer = message.serialize(); + + // Should have correct length + expect(buffer.length).toBe( + message.header.serializeSize + 2 + personaList.size(), + ); + + // Should write persona count at offset 12 + expect(buffer.readUInt16BE(12)).toBe(1); + + // Now test deserialization + const newMessage = new PersonaMapsMessage(); + // @ts-ignore: _header is protected + newMessage._header = new BytableHeader(); + newMessage.deserialize(buffer); + + expect(newMessage.raw).toEqual(buffer); + // The body should be set, but _personaRecords is not set by deserialize + // (since setBody is not implemented), so we only check raw and header + expect(newMessage.header).toBeDefined(); + }); + + it('should return correct JSON representation', () => { + const personaList = new PersonaList(); + const personaRecord = new PersonaRecord(); + personaRecord.customerId = 1; + personaRecord.personaId = 2; + personaRecord.personaName = 'Test'; + personaRecord.shardId = 3; + personaList.addPersonaRecord(personaRecord); + + message._personaRecords = personaList; + + const json = message.asJSON(); + expect(json).toHaveProperty('header'); + expect(json).toHaveProperty('personaRecords'); + expect(json.personaRecords).toBe(personaList); + }); + + it('should return correct string representation', () => { + const personaList = new PersonaList(); + message._personaRecords = personaList; + const str = message.toString(); + expect(str).toContain('PersonaMapsMessage'); + expect(str).toContain('personaRecords'); + }); + + describe('serializeString', () => { + it('should serialize string with length prefix', () => { + const str = 'hello'; + const buf = serializeString(str); + expect(buf.readUInt16BE(0)).toBe(str.length); + expect(buf.toString('utf8', 2)).toBe(str); + }); + + it('should correctly deserialize a buffer into a PersonaRecord', () => { + // Prepare a buffer with known values for all fields + const personaName = 'JohnDoe'; + const gameSerialNumber = 'SERIAL123'; + const hashedKey = 'HASHEDKEY'.padEnd(400, '\0'); + const extraData = Buffer.alloc(512, 0x11); + const personaData = Buffer.alloc(256, 0x22); + const pictureData = Buffer.from([0x33]); + + let offset = 0; + const buf = Buffer.alloc( + 4 + + 33 + + 4 + + 4 + + 4 + + 4 + + 4 + + 2 + + 4 + + 33 + + 4 + + 4 + + 512 + + 256 + + 1 + + 2 + + 4 + + 400 + + 2 + + 4, + ); + + buf.writeUInt32BE(42, offset); // customerId + offset += 4; + buf.write(personaName.padEnd(33, '\0'), offset, 'utf8'); // personaName + offset += 33; + buf.writeUInt32BE(1001, offset); // serverDataId + offset += 4; + buf.writeUInt32BE(2002, offset); // createDate + offset += 4; + buf.writeUInt32BE(3003, offset); // lastLogin + offset += 4; + buf.writeUInt32BE(4004, offset); // numberOfGames + offset += 4; + buf.writeUInt32BE(5005, offset); // personaId + offset += 4; + buf.writeUInt16BE(1, offset); // isOnline + offset += 2; + buf.writeUInt32BE(6006, offset); // purchaseTimestamp + offset += 4; + buf.write(gameSerialNumber.padEnd(33, '\0'), offset, 'utf8'); // gameSerialNumber + offset += 33; + buf.writeUInt32BE(7007, offset); // timeOnline + offset += 4; + buf.writeUInt32BE(8008, offset); // timeInGame + offset += 4; + extraData.copy(buf, offset); // extraData + offset += 512; + personaData.copy(buf, offset); // personaData + offset += 256; + pictureData.copy(buf, offset); // pictureData + offset += 1; + buf.writeUInt16BE(2, offset); // dnd + offset += 2; + buf.writeUInt32BE(9009, offset); // startedPlayingTimestamp + offset += 4; + buf.write(hashedKey, offset, 400, 'utf8'); // hashedKey + offset += 400; + buf.writeUInt16BE(3, offset); // personaLevel + offset += 2; + buf.writeUInt32BE(123456, offset); // shardId + + const record = new PersonaRecord().deserialize(buf); + + expect(record.customerId).toBe(42); + expect(record.personaName.replace(/\0+$/, '')).toBe(personaName); + expect(record.serverDataId).toBe(1001); + expect(record.createDate).toBe(2002); + expect(record.lastLogin).toBe(3003); + expect(record.numberOfGames).toBe(4004); + expect(record.personaId).toBe(5005); + expect(record.isOnline).toBe(1); + expect(record.purchaseTimestamp).toBe(6006); + expect(record.gameSerialNumber.replace(/\0+$/, '')).toBe( + gameSerialNumber, + ); + expect(record.timeOnline).toBe(7007); + expect(record.timeInGame).toBe(8008); + expect(record.extraData.equals(extraData)).toBe(true); + expect(record.personaData.equals(personaData)).toBe(true); + expect(record.pictureData.equals(pictureData)).toBe(true); + expect(record.dnd).toBe(2); + expect(record.startedPlayingTimestamp).toBe(9009); + expect(record.hashedKey.replace(/\0+$/, '')).toBe('HASHEDKEY'); + expect(record.personaLevel).toBe(3); + expect(record.shardId).toBe(123456); + }); + + it('should handle empty buffer gracefully (throws)', () => { + const record = new PersonaRecord(); + expect(() => record.deserialize(Buffer.alloc(0))).toThrow(); + }); + + it('should serialize PersonaRecord to correct buffer format', () => { + // @ts-ignore + PersonaRecord.size = () => 52; + const personaRecord = new PersonaRecord(); + personaRecord.customerId = 1234; + personaRecord.personaId = 5678; + personaRecord.personaName = 'TestName'; + personaRecord.shardId = 4321; + + const buf = personaRecord.serialize(); + console.log( + 'serialize correct format: buf.length =', + buf.length, + buf, + ); + + // Should be correct size + expect(buf.length).toBe(PersonaRecord.size()); + + let offset = 0; + expect(buf.readUInt32BE(offset)).toBe(1234); // customerId + offset += 4; + expect(buf.readUInt16BE(offset)).toBe(3341); // hardcoded value + offset += 2; + expect(buf.readUInt32BE(offset)).toBe(5678); // personaId + offset += 4; + expect(buf.readUInt32BE(offset)).toBe(4321); // shardId + offset += 4; + offset += 4; // unknown 4 bytes + + // personaName as length-prefixed string (2 bytes length + string) + const nameLen = buf.readUInt16BE(offset); + expect(nameLen).toBe('TestName'.length); + expect(buf.toString('utf8', offset + 2, offset + 2 + nameLen)).toBe( + 'TestName', + ); + }); + + it('should handle empty personaName in serialization', () => { + // @ts-ignore + PersonaRecord.size = () => 52; + const personaRecord = new PersonaRecord(); + personaRecord.customerId = 1; + personaRecord.personaId = 2; + personaRecord.personaName = ''; + personaRecord.shardId = 3; + + const buf = personaRecord.serialize(); + console.log('serialize empty name: buf.length =', buf.length, buf); + + // personaName length should be 0 + expect(buf.readUInt16BE(18)).toBe(0); + }); + + it('should throw and wrap error if serialization fails', () => { + const personaRecord = new PersonaRecord(); + const origSize = PersonaRecord.size; + // @ts-ignore + PersonaRecord.size = () => { + throw new Error('fail'); + }; + try { + expect(() => personaRecord.serialize()).toThrow( + /Error serializing PersonaRecord buffer/, + ); + } finally { + // @ts-ignore + PersonaRecord.size = origSize; + } + }); + }); +}); diff --git a/packages/persona/src/PersonaMapsMessage.ts b/packages/persona/src/PersonaMapsMessage.ts index f7feca5f4..e0094c382 100644 --- a/packages/persona/src/PersonaMapsMessage.ts +++ b/packages/persona/src/PersonaMapsMessage.ts @@ -1,358 +1,346 @@ -import { NPSMessage } from "rusty-motors-shared"; -import { NPSHeader } from "rusty-motors-shared"; +import { BytableMessage } from '@rustymotors/binary'; /** * * This is type UserGameData */ export class PersonaRecord { - customerId: number; - personaName: string; - serverDataId: number; - createDate: number; - lastLogin: number; - numberOfGames: number; - personaId: number; - isOnline: number; - purchaseTimestamp: number; - gameSerialNumber: string; - timeOnline: number; - timeInGame: number; - extraData: Buffer; - personaData: Buffer; - pictureData: Buffer; - dnd: number; - startedPlayingTimestamp: number; - hashedKey: string; - personaLevel: number; - shardId: number; - constructor() { - this.customerId = 0; - this.personaName = ""; - this.serverDataId = 0; - this.createDate = 0; - this.lastLogin = 0; - this.numberOfGames = 0; - this.personaId = 0; - this.isOnline = 0; - this.purchaseTimestamp = 0; - this.gameSerialNumber = ""; - this.timeOnline = 0; - this.timeInGame = 0; - this.extraData = Buffer.alloc(512); - this.personaData = Buffer.alloc(256); - this.pictureData = Buffer.alloc(1); - this.dnd = 0; - this.startedPlayingTimestamp = 0; - this.hashedKey = ""; - this.personaLevel = 0; - this.shardId = 0; - } + customerId: number; + personaName: string; + serverDataId: number; + createDate: number; + lastLogin: number; + numberOfGames: number; + personaId: number; + isOnline: number; + purchaseTimestamp: number; + gameSerialNumber: string; + timeOnline: number; + timeInGame: number; + extraData: Buffer; + personaData: Buffer; + pictureData: Buffer; + dnd: number; + startedPlayingTimestamp: number; + hashedKey: string; + personaLevel: number; + shardId: number; + constructor() { + this.customerId = 0; + this.personaName = ''; + this.serverDataId = 0; + this.createDate = 0; + this.lastLogin = 0; + this.numberOfGames = 0; + this.personaId = 0; + this.isOnline = 0; + this.purchaseTimestamp = 0; + this.gameSerialNumber = ''; + this.timeOnline = 0; + this.timeInGame = 0; + this.extraData = Buffer.alloc(512); + this.personaData = Buffer.alloc(256); + this.pictureData = Buffer.alloc(1); + this.dnd = 0; + this.startedPlayingTimestamp = 0; + this.hashedKey = ''; + this.personaLevel = 0; + this.shardId = 0; + } - /** - * - * @param {Buffer} buffer - * @returns {PersonaRecord} - */ - deserialize(buffer: Buffer): PersonaRecord { - let offset = 0; - this.customerId = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.personaName = buffer.toString("utf8", offset, offset + 33); // 33 - offset += 33; - this.serverDataId = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.createDate = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.lastLogin = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.numberOfGames = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.personaId = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.isOnline = buffer.readUInt16BE(offset); // 2 - offset += 2; - this.purchaseTimestamp = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.gameSerialNumber = buffer - .subarray(offset, offset + 33) - .toString("utf8"); // 33 - offset += 33; - this.timeOnline = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.timeInGame = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.extraData = buffer.subarray(offset, offset + 512); // 512 - offset += 512; - this.personaData = buffer.subarray(offset, offset + 256); // 256 - offset += 256; - this.pictureData = buffer.subarray(offset, offset + 1); // 1 - offset += 1; - this.dnd = buffer.readUInt16BE(offset); // 2 - offset += 2; - this.startedPlayingTimestamp = buffer.readUInt32BE(offset); // 4 - offset += 4; - this.hashedKey = buffer.subarray(offset, offset + 400).toString("utf8"); // 400 - offset += 400; - this.personaLevel = buffer.readUInt16BE(offset); // 2 - offset += 2; - this.shardId = buffer.readUInt32BE(offset); // 2 - // Offset 1285 - return this; - } + /** + * + * @param {Buffer} buffer + * @returns {PersonaRecord} + */ + deserialize(buffer: Buffer): PersonaRecord { + let offset = 0; + this.customerId = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.personaName = buffer.toString('utf8', offset, offset + 33); // 33 + offset += 33; + this.serverDataId = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.createDate = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.lastLogin = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.numberOfGames = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.personaId = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.isOnline = buffer.readUInt16BE(offset); // 2 + offset += 2; + this.purchaseTimestamp = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.gameSerialNumber = buffer + .subarray(offset, offset + 33) + .toString('utf8'); // 33 + offset += 33; + this.timeOnline = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.timeInGame = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.extraData = buffer.subarray(offset, offset + 512); // 512 + offset += 512; + this.personaData = buffer.subarray(offset, offset + 256); // 256 + offset += 256; + this.pictureData = buffer.subarray(offset, offset + 1); // 1 + offset += 1; + this.dnd = buffer.readUInt16BE(offset); // 2 + offset += 2; + this.startedPlayingTimestamp = buffer.readUInt32BE(offset); // 4 + offset += 4; + this.hashedKey = buffer.subarray(offset, offset + 400).toString('utf8'); // 400 + offset += 400; + this.personaLevel = buffer.readUInt16BE(offset); // 2 + offset += 2; + this.shardId = buffer.readUInt32BE(offset); // 2 + // Offset 1285 + return this; + } - /** - * - * @returns {Buffer} - */ - serialize(): Buffer { - const buffer = Buffer.alloc(PersonaRecord.size()); - try { - let offset = 0; - buffer.writeUInt32BE(this.customerId, offset); // 4 - Unknown if this is correct - offset += 4; // offset = 4 - buffer.writeUInt16BE(3341, offset); // 2 - unknown if this is correct - offset += 2; // offset = 6 - buffer.writeUInt32BE(this.personaId, offset); // 4 - Known to be correct - offset += 4; // offset = 10 - buffer.writeUInt32BE(this.shardId, offset); // 4 - Known to be correct - offset += 4; // offset = 14 - // We don't know what goes here yet - offset += 4; // offset = 18 - serializeString(this.personaName).copy(buffer, offset); // 34 - Known to be correct - // offset = 52 + /** + * + * @returns {Buffer} + */ + serialize(): Buffer { + try { + const size = PersonaRecord.size(); + const buffer = Buffer.alloc(size); + if (size === 0) return buffer; + let offset = 0; + buffer.writeUInt32BE(this.customerId, offset); // 4 - Unknown if this is correct + offset += 4; // offset = 4 + buffer.writeUInt16BE(3341, offset); // 2 - unknown if this is correct + offset += 2; // offset = 6 + buffer.writeUInt32BE(this.personaId, offset); // 4 - Known to be correct + offset += 4; // offset = 10 + buffer.writeUInt32BE(this.shardId, offset); // 4 - Known to be correct + offset += 4; // offset = 14 + // We don't know what goes here yet + offset += 4; // offset = 18 + // Write personaName as 2-byte length + up to 32 bytes UTF-8, padded with zeros + const nameBuf = Buffer.alloc(34, 0); // 2 bytes length + 32 bytes name + const nameLen = Buffer.byteLength(this.personaName, 'utf8'); + nameBuf.writeUInt16BE(nameLen, 0); + if (nameLen > 0) { + nameBuf.write(this.personaName, 2, 32, 'utf8'); + } + nameBuf.copy(buffer, offset); + // offset = 52 + return buffer; + } catch (error) { + if (error instanceof Error) { + const err = new Error( + `Error serializing PersonaRecord buffer: ${error.message}`, + ); + err.cause = error; + throw err; + } else { + const err = new Error( + `Error serializing PersonaRecord buffer: ${String(error)}`, + ); + err.cause = error; + throw err; + } + } + } - // buffer.writeUInt32BE(this.serverDataId, offset); // 4 - // offset += 4; // offset = 56 - // buffer.writeUInt32BE(this.createDate, offset); // 4 - // offset += 4; // offset = 60 - // buffer.writeUInt32BE(this.lastLogin, offset); // 4 - // offset += 4; // offset = 64 - // buffer.writeUInt32BE(this.numberOfGames, offset); // 4 (Max personas)) - // offset += 4; // offset = 68 - // buffer.writeUInt16BE(this.isOnline, offset); // 2 - // offset += 2; // offset = 70 - // buffer.writeUInt32BE(this.purchaseTimestamp, offset); // 4 - // offset += 4; // offset = 74 - // buffer.write(this.gameSerialNumber, offset, 33, "utf8"); // 33 - // offset += 33; // offset = 107 - // buffer.writeUInt32BE(this.timeOnline, offset); // 4 - // offset += 4; // offset = 111 - // buffer.writeUInt32BE(this.timeInGame, offset); // 4 - // offset += 4; // offset = 115 - // this.extraData.copy(buffer, offset, 512); // 512 - // offset += 512; // offset = 627 - // this.personaData.copy(buffer, offset, 256); // 256 - // offset += 256; // offset = 883 - // this.pictureData.copy(buffer, offset, 1); // 1 - // offset += 1; // offset = 884 - // buffer.writeUInt16BE(this.dnd, offset); // 2 - // offset += 2; // offset = 886 - // buffer.writeUInt32BE(this.startedPlayingTimestamp, offset); // 4 - // offset += 4; // offset = 890 - // buffer.write(this.hashedKey, offset, 400, "utf8"); // 400 - // offset += 400; // offset = 1290 - // buffer.writeUInt16BE(this.personaLevel, offset); // 2 - // offset += 2; // offset = 1292 - } catch (error) { - const err = Error( - `Error serializing PersonaRecord buffer: ${String(error)}`, - ); - err.cause = error; - throw err; - } - return buffer; - } + static size() { + return 52; + } - static size() { - return 52; - } + toJSON() { + return { + customerId: this.customerId, + personaId: this.personaId, + personaName: this.personaName, + shardId: this.shardId, + serverDataId: this.serverDataId, + // createDate: this.createDate, + // lastLogin: this.lastLogin, + // numberOfGames: this.numberOfGames, + // isOnline: this.isOnline, + // purchaseTimestamp: this.purchaseTimestamp, + // gameSerialNumber: this.gameSerialNumber, + // timeOnline: this.timeOnline, + // timeInGame: this.timeInGame, + // extraData: this.extraData, + // personaData: this.personaData, + // pictureData: this.pictureData, + // dnd: this.dnd, + // startedPlayingTimestamp: this.startedPlayingTimestamp, + // hashedKey: this.hashedKey, + // personaLevel: this.personaLevel, + }; + } - toJSON() { - return { - customerId: this.customerId, - personaId: this.personaId, - personaName: this.personaName, - shardId: this.shardId, - serverDataId: this.serverDataId, - // createDate: this.createDate, - // lastLogin: this.lastLogin, - // numberOfGames: this.numberOfGames, - // isOnline: this.isOnline, - // purchaseTimestamp: this.purchaseTimestamp, - // gameSerialNumber: this.gameSerialNumber, - // timeOnline: this.timeOnline, - // timeInGame: this.timeInGame, - // extraData: this.extraData, - // personaData: this.personaData, - // pictureData: this.pictureData, - // dnd: this.dnd, - // startedPlayingTimestamp: this.startedPlayingTimestamp, - // hashedKey: this.hashedKey, - // personaLevel: this.personaLevel, - }; - } + asJSON() { + return this.toJSON(); + } - asJSON() { - return this.toJSON(); - } - - toString() { - return `PersonaRecord: ${JSON.stringify(this.toJSON())}`; - } + toString() { + return `PersonaRecord: ${JSON.stringify(this.toJSON())}`; + } } export class PersonaList { - _personaRecords: PersonaRecord[]; - constructor() { - /** @type {PersonaRecord[]} */ - this._personaRecords = []; - } + _personaRecords: PersonaRecord[]; + constructor() { + /** @type {PersonaRecord[]} */ + this._personaRecords = []; + } - /** - * - * @param {Buffer} buffer - * @returns {PersonaList} - */ - deserialize(buffer: Buffer): PersonaList { - let offset = 0; - const personaRecordCount = buffer.readUInt16BE(offset); - offset += 2; - for (let i = 0; i < personaRecordCount; i++) { - const personaRecord = new PersonaRecord(); - personaRecord.deserialize(buffer.subarray(offset)); - offset += PersonaRecord.size(); - this._personaRecords.push(personaRecord); - } - return this; - } + /** + * + * @param {Buffer} buffer + * @returns {PersonaList} + */ + deserialize(buffer: Buffer): PersonaList { + let offset = 0; + const personaRecordCount = buffer.readUInt16BE(offset); + offset += 2; + for (let i = 0; i < personaRecordCount; i++) { + const personaRecord = new PersonaRecord(); + personaRecord.deserialize(buffer.subarray(offset)); + offset += PersonaRecord.size(); + this._personaRecords.push(personaRecord); + } + return this; + } - /** - * - * @returns {Buffer} - */ - serialize(): Buffer { - const NEEDED_SIZE = PersonaRecord.size() * this._personaRecords.length; - const buffer = Buffer.alloc(NEEDED_SIZE); - try { - let offset = 0; - if (!this._personaRecords) { - throw Error("PersonaRecords is undefined"); - } - for (const personaRecord of this._personaRecords) { - personaRecord.serialize().copy(buffer, offset); - offset += PersonaRecord.size(); - } - } catch (error) { - throw Error(`Error serializing PersonaList buffer: ${String(error)}`); - } - return buffer; - } + /** + * + * @returns {Buffer} + */ + serialize(): Buffer { + const NEEDED_SIZE = PersonaRecord.size() * this._personaRecords.length; + const buffer = Buffer.alloc(NEEDED_SIZE); + try { + let offset = 0; + if (!this._personaRecords) { + throw Error('PersonaRecords is undefined'); + } + for (const personaRecord of this._personaRecords) { + personaRecord.serialize().copy(buffer, offset); + offset += PersonaRecord.size(); + } + } catch (error) { + throw Error( + `Error serializing PersonaList buffer: ${String(error)}`, + ); + } + return buffer; + } - /** - * @param {PersonaRecord} personaRecord - */ - addPersonaRecord(personaRecord: PersonaRecord) { - this._personaRecords.push(personaRecord); - } + /** + * @param {PersonaRecord} personaRecord + */ + addPersonaRecord(personaRecord: PersonaRecord) { + this._personaRecords.push(personaRecord); + } - personaCount() { - return this._personaRecords.length; - } + personaCount() { + return this._personaRecords.length; + } - size() { - return PersonaRecord.size() * this._personaRecords.length; - } + size() { + return PersonaRecord.size() * this._personaRecords.length; + } - asJSON() { - return { - personaRecords: this._personaRecords, - }; - } + asJSON() { + return { + personaRecords: this._personaRecords, + }; + } - toString() { - return `PersonaList: ${JSON.stringify(this._personaRecords)}`; - } + toString() { + return `PersonaList: ${JSON.stringify(this._personaRecords)}`; + } } -export class PersonaMapsMessage extends NPSMessage { - _personaRecords: PersonaList | undefined; - raw: Buffer | undefined; - constructor() { - super(); - /** @type {PersonaList | undefined} */ - this._personaRecords = undefined; - } +export class PersonaMapsMessage extends BytableMessage { + _personaRecords: PersonaList | undefined; + raw: Buffer | undefined; + constructor() { + super(); + /** @type {PersonaList | undefined} */ + this._personaRecords = undefined; + } - /** - * @param {Buffer} buffer - * @returns {PersonaMapsMessage} - */ - deserialize(buffer: Buffer): PersonaMapsMessage { - try { - this._header._doDeserialize(buffer); - this.setBuffer(buffer.subarray(NPSHeader.size())); - this.raw = buffer; - return this; - } catch (error) { - const err = Error( - `Error deserializing PersonaMapsMessage: ${String(error)}`, - ); - err.cause = error; - throw err; - } - } + /** + * @param {Buffer} buffer + * @returns {PersonaMapsMessage} + */ + override deserialize(buffer: Buffer): this { + try { + this.header.deserialize(buffer); + this.setBody(buffer.subarray(this.header.serializeSize)); + this.raw = buffer; + return this; + } catch (error) { + const err = Error( + `Error deserializing PersonaMapsMessage: ${String(error)}`, + ); + err.cause = error; + throw err; + } + } - /** - * @returns {Buffer} - */ - override serialize(): Buffer { - try { - if (!this._personaRecords) { - throw Error("PersonaRecords is undefined"); - } - this._header.length = NPSHeader.size() + 2 + this._personaRecords.size(); - const buffer = Buffer.alloc(this._header.length); - this._header._doSerialize().copy(buffer); + /** + * @returns {Buffer} + */ + override serialize(): Buffer { + try { + if (!this._personaRecords) { + throw Error('PersonaRecords is undefined'); + } + this.header.setMessageLength( + this.header.serializeSize + 2 + this._personaRecords.size(), + ); + const buffer = Buffer.alloc(this.header.messageLength); + this.header.serialize().copy(buffer); - // Write the persona count. This is known to be correct at offset 12 - buffer.writeUInt16BE(this._personaRecords.personaCount(), 12); - // This is a serialized PersonaList - this.data.copy(buffer, NPSHeader.size() + 2); - return buffer; - } catch (error) { - const err = Error( - `Error serializing PersonaMapsMessage: ${String(error)}`, - ); - err.cause = error; - throw err; - } - } + // Write the persona count. This is known to be correct at offset 12 + buffer.writeUInt16BE(this._personaRecords.personaCount(), 12); + // This is a serialized PersonaList + this.data.copy(buffer, this.header.serializeSize + 2); + return buffer; + } catch (error) { + const err = Error( + `Error serializing PersonaMapsMessage: ${String(error)}`, + ); + err.cause = error; + throw err; + } + } - asJSON() { - return { - header: this._header, - personaRecords: this._personaRecords, - }; - } + asJSON() { + return { + header: this.header, + personaRecords: this._personaRecords, + }; + } - override toString() { - return `PersonaMapsMessage: ${JSON.stringify({ - header: this._header, - personaRecords: this._personaRecords, - })}`; - } + override toString() { + return `PersonaMapsMessage: ${JSON.stringify({ + header: this.header, + personaRecords: this._personaRecords, + })}`; + } } /** - * Serializes a string to a buffer. The buffer will be prefixed with the length of the string. - * @param {string} str - * @returns {Buffer} + * Serializes a string into a buffer with a 2-byte big-endian length prefix. + * + * @param str - The string to serialize. + * @returns A buffer containing the length-prefixed UTF-8 encoded string. */ export function serializeString(str: string): Buffer { - const buf = Buffer.alloc(str.length + 2); + const buf = Buffer.alloc(str.length + 2); - buf.writeUInt16BE(str.length); - buf.write(str, 2); + buf.writeUInt16BE(str.length); + buf.write(str, 2); - return buf; + return buf; } diff --git a/packages/persona/src/_gameLogout.ts b/packages/persona/src/_gameLogout.ts index 76ead3809..e352cbe58 100644 --- a/packages/persona/src/_gameLogout.ts +++ b/packages/persona/src/_gameLogout.ts @@ -1,7 +1,6 @@ -import { SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { getServerLogger } from "rusty-motors-shared"; - +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** * Handle game logout @@ -16,30 +15,34 @@ import { getServerLogger } from "rusty-motors-shared"; */ export async function _gameLogout({ - connectionId, - message, - log = getServerLogger( "persona._gameLogout"), + connectionId, + message, + log = getServerLogger('persona._gameLogout'), }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - const requestPacket = message; - log.debug(`[${connectionId}] _npsLogoutGameUser request: ${requestPacket.toHexString()}`); + const requestPacket = message; + log.debug( + `[${connectionId}] _npsLogoutGameUser request: ${requestPacket.toHexString()}`, + ); - // Build the packet - const responsePacket = new LegacyMessage(); - responsePacket._header.id = 519; - log.debug(`[${connectionId}] _npsLogoutGameUser response: ${responsePacket.toHexString()}`); + // Build the packet + const responsePacket = new LegacyMessage(); + responsePacket._header.id = 519; + log.debug( + `[${connectionId}] _npsLogoutGameUser response: ${responsePacket.toHexString()}`, + ); - const outboundMessage = new SerializedBufferOld(); - outboundMessage._doDeserialize(responsePacket._doSerialize()); + const outboundMessage = new SerializedBufferOld(); + outboundMessage._doDeserialize(responsePacket._doSerialize()); - return { - connectionId, - messages: [outboundMessage], - }; + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/persona/src/_getFirstBuddy.ts b/packages/persona/src/_getFirstBuddy.ts index 46ed8601a..0daafefbf 100644 --- a/packages/persona/src/_getFirstBuddy.ts +++ b/packages/persona/src/_getFirstBuddy.ts @@ -1,113 +1,124 @@ -import { SerializedBufferOld } from "rusty-motors-shared"; -import { NPSMessage } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { BuddyCount, BuddyInfoMessage, BuddyList } from "./BuddyInfoMessage.js"; -import { getServerLogger, ServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("PersonaServer"); - +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { NPSMessage } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { BuddyCount, BuddyInfoMessage, BuddyList } from './BuddyInfoMessage.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; + +const defaultLogger = getServerLogger('PersonaServer'); + +/** + * Processes a legacy buddy information request and returns a serialized buddy count message. + * + * Deserializes the incoming message to extract the persona ID, constructs a buddy count message (currently always zero), and returns it as a serialized response. + * + * @param connectionId - The identifier for the client connection. + * @param message - The incoming legacy message containing the buddy request. + * @returns An object containing the {@link connectionId} and an array with the serialized buddy count message. + * + * @remark Only the buddy count message is returned; buddy details are constructed but not sent. The buddy list is currently hardcoded and not looked up by persona ID. + */ export async function _getFirstBuddy({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - // This message is a versioned nps message - const incomingMessage = new NPSMessage(); - incomingMessage._doDeserialize(message._doSerialize()); - - log.debug( - `in _getFirstBuddy, incomingMessage: ${incomingMessage - .serialize() - .toString("hex")}`, - ); - - // extract the personaId - const personaId = incomingMessage.data.readUInt32BE(0); - - log.debug(`in _getFirstBuddy, personaId: ${personaId}`); - - // TODO: Here we need to look up the buddies for the personaId - - // First, send the BuddyCount message - const buddyCountMessage = new BuddyCount(); - buddyCountMessage.buddyCount = 0; - - const outboundMessage1 = new SerializedBufferOld(); - outboundMessage1._doDeserialize(buddyCountMessage.serialize()); - - const buddyInfoMessage = new BuddyInfoMessage(); - - for (const buddy of buddies) { - const buddyInfo = new BuddyList(); - buddyInfo.buddyName = buddy.buddyName; - buddyInfo.gameName = buddy.gameName; - buddyInfo.isBuddy = buddy.isBuddy; - buddyInfo.isOnline = buddy.isOnline; - buddyInfo.dnd = buddy.dnd; - buddyInfo.dnb = buddy.dnb; - buddyInfo.noEntry = buddy.noEntry; - buddyInfo.muteWhispers = buddy.muteWhispers; - buddyInfo.muteChat = buddy.muteChat; - - buddyInfoMessage.add(buddyInfo); - } - - const outboundMessage = new SerializedBufferOld(); - outboundMessage._doDeserialize(buddyInfoMessage.serialize()); - - log.debug( - `in _getFirstBuddy, outboundMessage: ${outboundMessage1.toString()}`, - ); - - return { - connectionId, - messages: [outboundMessage1], - }; + // This message is a versioned nps message + const incomingMessage = new NPSMessage(); + incomingMessage.deserialize(message.serialize()); + + log.debug( + `in _getFirstBuddy, incomingMessage: ${incomingMessage + .serialize() + .toString('hex')}`, + ); + + // extract the personaId + const personaId = incomingMessage.data.readUInt32BE(0); + + log.debug(`in _getFirstBuddy, personaId: ${personaId}`); + + // TODO: Here we need to look up the buddies for the personaId + + // First, send the BuddyCount message + const buddyCountMessage = new BuddyCount(); + buddyCountMessage.buddyCount = 0; + + const outboundMessage1 = new SerializedBufferOld(); + outboundMessage1._doDeserialize(buddyCountMessage.serialize()); + + const buddyInfoMessage = new BuddyInfoMessage(); + + for (const buddy of buddies) { + const buddyInfo = new BuddyList(); + buddyInfo.buddyName = buddy.buddyName; + buddyInfo.gameName = buddy.gameName; + buddyInfo.isBuddy = buddy.isBuddy; + buddyInfo.isOnline = buddy.isOnline; + buddyInfo.dnd = buddy.dnd; + buddyInfo.dnb = buddy.dnb; + buddyInfo.noEntry = buddy.noEntry; + buddyInfo.muteWhispers = buddy.muteWhispers; + buddyInfo.muteChat = buddy.muteChat; + + buddyInfoMessage.add(buddyInfo); + } + + const outboundMessage = new SerializedBufferOld(); + outboundMessage._doDeserialize(buddyInfoMessage.serialize()); + + log.debug( + `in _getFirstBuddy, outboundMessage: ${outboundMessage1.toString()}`, + ); + + return { + connectionId, + messages: [outboundMessage1], + }; } interface BuddyInfoRecord { - buddyId: number; - buddyName: string; - gameName: string; - isBuddy: boolean; - isOnline: boolean; - dnd: boolean; - dnb: boolean; - noEntry: boolean; - muteWhispers: boolean; - muteChat: boolean; + buddyId: number; + buddyName: string; + gameName: string; + isBuddy: boolean; + isOnline: boolean; + dnd: boolean; + dnb: boolean; + noEntry: boolean; + muteWhispers: boolean; + muteChat: boolean; } export const buddies: BuddyInfoRecord[] = [ - { - buddyId: 2, - buddyName: "Einstein", - gameName: "Good Woof", - isBuddy: true, - isOnline: true, - dnd: false, - dnb: false, - noEntry: false, - muteWhispers: false, - muteChat: false, - }, - { - buddyId: 3, - buddyName: "Marty", - gameName: "That kid", - isBuddy: true, - isOnline: true, - dnd: false, - dnb: false, - noEntry: false, - muteWhispers: false, - muteChat: false, - }, + { + buddyId: 2, + buddyName: 'Einstein', + gameName: 'Good Woof', + isBuddy: true, + isOnline: true, + dnd: false, + dnb: false, + noEntry: false, + muteWhispers: false, + muteChat: false, + }, + { + buddyId: 3, + buddyName: 'Marty', + gameName: 'That kid', + isBuddy: true, + isOnline: true, + dnd: false, + dnb: false, + noEntry: false, + muteWhispers: false, + muteChat: false, + }, ]; diff --git a/packages/persona/src/_selectGamePersona.ts b/packages/persona/src/_selectGamePersona.ts index e1928a346..cd1c30838 100644 --- a/packages/persona/src/_selectGamePersona.ts +++ b/packages/persona/src/_selectGamePersona.ts @@ -1,8 +1,8 @@ -import { SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { getServerLogger } from "rusty-motors-shared"; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("PersonaServer"); +const defaultLogger = getServerLogger('PersonaServer'); /** * Selects a game persona and marks it as in use @@ -17,45 +17,45 @@ const defaultLogger = getServerLogger("PersonaServer"); */ export async function _selectGamePersona({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - log.debug("_npsSelectGamePersona..."); - const requestPacket = message; - log.debug( - `LegacyMsg request object from _npsSelectGamePersona ${requestPacket - ._doSerialize() - .toString("hex")}`, - ); + log.debug('_npsSelectGamePersona...'); + const requestPacket = message; + log.debug( + `LegacyMsg request object from _npsSelectGamePersona ${requestPacket + ._doSerialize() + .toString('hex')}`, + ); - // Create the packet content - const packetContent = Buffer.alloc(251); + // Create the packet content + const packetContent = Buffer.alloc(251); - // Build the packet - // Response Code - // 207 = success - const responsePacket = new LegacyMessage(); - responsePacket._header.id = 519; - responsePacket.setBuffer(packetContent); - log.debug( - `LegacyMsg response object from _npsSelectGamePersona ${responsePacket - ._doSerialize() - .toString("hex")} `, - ); + // Build the packet + // Response Code + // 207 = success + const responsePacket = new LegacyMessage(); + responsePacket._header.id = 519; + responsePacket.setBuffer(packetContent); + log.debug( + `LegacyMsg response object from _npsSelectGamePersona ${responsePacket + ._doSerialize() + .toString('hex')} `, + ); - const outboundMessage = new SerializedBufferOld(); - outboundMessage.setBuffer(responsePacket._doSerialize()); + const outboundMessage = new SerializedBufferOld(); + outboundMessage.setBuffer(responsePacket._doSerialize()); - return { - connectionId, - messages: [outboundMessage], - }; + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/persona/src/handlers/getPersonaInfo.ts b/packages/persona/src/handlers/getPersonaInfo.ts index 5b13edc95..e6c23a6f9 100644 --- a/packages/persona/src/handlers/getPersonaInfo.ts +++ b/packages/persona/src/handlers/getPersonaInfo.ts @@ -1,31 +1,46 @@ -import { LegacyMessage, NPSMessage, SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { createGameProfile } from "rusty-motors-nps"; -import { getPersonasByPersonaId } from "../getPersonasByPersonaId.js"; -import { personaToString } from "../internal.js"; +import { + LegacyMessage, + NPSMessage, + SerializedBufferOld, +} from 'rusty-motors-shared'; +import { createGameProfile } from 'rusty-motors-nps'; +import { getPersonasByPersonaId } from '../getPersonasByPersonaId.js'; +import { personaToString } from '../internal.js'; -import { getServerLogger } from "rusty-motors-shared"; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("PersonaServer"); +const defaultLogger = getServerLogger('PersonaServer'); +/** + * Processes a legacy message to retrieve persona information and returns a serialized response. + * + * Deserializes the incoming {@link message}, extracts the persona ID, fetches the corresponding persona, and constructs a response containing the persona's profile information. The response is serialized and returned as an outbound message. + * + * @param connectionId - The identifier for the client connection. + * @param message - The incoming legacy message containing the persona request. + * @returns An object with the original {@link connectionId} and an array containing the serialized response message. + * + * @throws {Error} If no persona is found for the extracted persona ID. + */ export async function getPersonaInfo({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - log.debug("getPersonaInfo..."); - const requestPacket = new NPSMessage(); - requestPacket._doDeserialize(message.serialize()); + log.debug('getPersonaInfo...'); + const requestPacket = new NPSMessage(); + requestPacket.deserialize(message.serialize()); - log.debug( - `LegacyMsg request object from getPersonaInfo ${requestPacket.toString()}`, - ); + log.debug( + `LegacyMsg request object from getPersonaInfo ${requestPacket.toString()}`, + ); const personaId = requestPacket.data.readUInt32BE(0); @@ -39,7 +54,7 @@ export async function getPersonaInfo({ throw new Error(`Persona not found for personaId: ${personaId}`); } - log.debug(`Persona found: ${personaToString(persona[0])}`); + log.debug(`Persona found: ${personaToString(persona[0])}`); const profile = createGameProfile(); @@ -47,23 +62,23 @@ export async function getPersonaInfo({ profile.profileId = persona[0]!.personaId; profile.profileName = persona[0]!.personaName; - // Build the packet - // Response Code - // 0x607 = Game Persona Info - const responsePacket = new LegacyMessage(); - responsePacket._header.id = 0x607; - responsePacket.setBuffer(profile.serialize()); - log.debug( - `LegacyMsg response object from getPersonaInfo ${responsePacket - ._doSerialize() - .toString("hex")} `, - ); + // Build the packet + // Response Code + // 0x607 = Game Persona Info + const responsePacket = new LegacyMessage(); + responsePacket._header.id = 0x607; + responsePacket.setBuffer(profile.serialize()); + log.debug( + `LegacyMsg response object from getPersonaInfo ${responsePacket + ._doSerialize() + .toString('hex')} `, + ); - const outboundMessage = new SerializedBufferOld(); - outboundMessage.setBuffer(responsePacket._doSerialize()); + const outboundMessage = new SerializedBufferOld(); + outboundMessage.setBuffer(responsePacket._doSerialize()); - return { - connectionId, - messages: [outboundMessage], - }; + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/persona/src/handlers/validatePersonaName.ts b/packages/persona/src/handlers/validatePersonaName.ts index 34115cb3e..6d70b695a 100644 --- a/packages/persona/src/handlers/validatePersonaName.ts +++ b/packages/persona/src/handlers/validatePersonaName.ts @@ -1,51 +1,51 @@ -import { SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; -import { RawMessage } from "rusty-motors-shared"; -import { getServerLogger } from "rusty-motors-shared"; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; +import { RawMessage } from 'rusty-motors-shared'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("PersonaServer"); +const defaultLogger = getServerLogger('PersonaServer'); /** * Check if a new persona name is valid */ export async function validatePersonaName({ - connectionId, - message, - log = defaultLogger, + connectionId, + message, + log = defaultLogger, }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - log.debug("validatePersonaName called"); - const requestPacket = message; - log.debug( - `NPSMsg request object from validatePersonaName ${requestPacket.toString()}`, - ); + log.debug('validatePersonaName called'); + const requestPacket = message; + log.debug( + `NPSMsg request object from validatePersonaName ${requestPacket.toString()}`, + ); - enum responseCodes { - NPS_DUP_USER = 0x20a, - NPS_USER_VALID = 0x601, - } + enum responseCodes { + NPS_DUP_USER = 0x20a, + NPS_USER_VALID = 0x601, + } - // Build the packet - const responsePacket = new RawMessage(responseCodes.NPS_DUP_USER); - log.debug( - `NPSMsg response object from validatePersonaName + // Build the packet + const responsePacket = new RawMessage(responseCodes.NPS_DUP_USER); + log.debug( + `NPSMsg response object from validatePersonaName ${JSON.stringify({ - NPSMsg: responsePacket.toString(), - })}`, - ); + NPSMsg: responsePacket.toString(), + })}`, + ); - const outboundMessage = new SerializedBufferOld(); - outboundMessage._doDeserialize(responsePacket.serialize()); + const outboundMessage = new SerializedBufferOld(); + outboundMessage._doDeserialize(responsePacket.serialize()); - return { - connectionId, - messages: [outboundMessage], - }; + return { + connectionId, + messages: [outboundMessage], + }; } diff --git a/packages/persona/src/internal.ts b/packages/persona/src/internal.ts index 5630ffcdc..61b87911b 100644 --- a/packages/persona/src/internal.ts +++ b/packages/persona/src/internal.ts @@ -14,20 +14,19 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { SerializedBufferOld, ServerLogger } from "rusty-motors-shared"; -import { LegacyMessage } from "rusty-motors-shared"; +import { SerializedBufferOld } from 'rusty-motors-shared'; +import { LegacyMessage } from 'rusty-motors-shared'; import { - PersonaList, - PersonaMapsMessage, - PersonaRecord, -} from "./PersonaMapsMessage.js"; -import { _gameLogout } from "./_gameLogout.js"; -import { _getFirstBuddy } from "./_getFirstBuddy.js"; -import { _selectGamePersona } from "./_selectGamePersona.js"; -import { validatePersonaName } from "./handlers/validatePersonaName.js"; -import { getPersonaInfo } from "./handlers/getPersonaInfo.js"; -import { getServerLogger } from "rusty-motors-shared"; - + PersonaList, + PersonaMapsMessage, + PersonaRecord, +} from './PersonaMapsMessage.js'; +import { _gameLogout } from './_gameLogout.js'; +import { _getFirstBuddy } from './_getFirstBuddy.js'; +import { _selectGamePersona } from './_selectGamePersona.js'; +import { validatePersonaName } from './handlers/validatePersonaName.js'; +import { getPersonaInfo } from './handlers/getPersonaInfo.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** * Array of supported message handlers @@ -45,56 +44,62 @@ import { getServerLogger } from "rusty-motors-shared"; * }>}[]} */ export const messageHandlers: { - opCode: number; - name: string; - handler: (args: { - connectionId: string; - message: LegacyMessage; - log: ServerLogger; - }) => Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; - }>; + opCode: number; + name: string; + handler: (args: { + connectionId: string; + message: LegacyMessage; + log: ServerLogger; + }) => Promise<{ + connectionId: string; + messages: SerializedBufferOld[]; + }>; }[] = [ - { - opCode: 1283, // 0x503 - name: "Game login", - handler: _selectGamePersona, - }, - { - opCode: 1295, // 0x50F - name: "Game logout", - handler: _gameLogout, - }, - { - opCode: 1305, // 0x519 - name: "Get persona info", - handler: getPersonaInfo, - }, - { - opCode: 1330, // 0x532 - name: "Get persona maps", - handler: getPersonaMaps, - }, - { - opCode: 1331, // 0x533 - name: "Validate persona name", - handler: validatePersonaName, - }, - { - opCode: 1291, // 0x50B - name: "Get first buddy", - handler: _getFirstBuddy, - }, + { + opCode: 1283, // 0x503 + name: 'Game login', + handler: _selectGamePersona, + }, + { + opCode: 1295, // 0x50F + name: 'Game logout', + handler: _gameLogout, + }, + { + opCode: 1305, // 0x519 + name: 'Get persona info', + handler: getPersonaInfo, + }, + { + opCode: 1330, // 0x532 + name: 'Get persona maps', + handler: getPersonaMaps, + }, + { + opCode: 1331, // 0x533 + name: 'Validate persona name', + handler: validatePersonaName, + }, + { + opCode: 1291, // 0x50B + name: 'Get first buddy', + handler: _getFirstBuddy, + }, ]; /** - * Return string as buffer + * Creates a fixed-size buffer containing the UTF-8 encoded bytes of a string. + * + * The resulting buffer will be exactly {@link size} bytes long, with the string's bytes at the start and any remaining space zero-filled. + * + * @param name - The string to encode into the buffer. + * @param size - The desired length of the output buffer. + * @returns A buffer of length {@link size} containing the encoded string. */ export function generateNameBuffer(name: string, size: number): Buffer { - const nameBuffer = Buffer.alloc(size); - Buffer.from(name, "utf8").copy(nameBuffer); - return nameBuffer; + const nameBuffer = Buffer.alloc(size); + Buffer.from(name, 'utf8').copy(nameBuffer); + return nameBuffer; } /** @@ -103,164 +108,181 @@ export function generateNameBuffer(name: string, size: number): Buffer { * @type {PersonaRecord[]} */ export const personaRecords: Pick< - PersonaRecord, - "customerId" | "personaId" | "personaName" | "shardId" + PersonaRecord, + 'customerId' | 'personaId' | 'personaName' | 'shardId' >[] = [ - { - customerId: 2868969472, - personaId: 20, - personaName: "Molly", - shardId: 44, - }, - { - customerId: 5551212, // 0x54 0xB4 0x6C - personaId: 21, - personaName: "Dr Brown", - shardId: 44, - }, - { - customerId: 0, - personaId: 22, - personaName: "Admin", - shardId: 44, - }, + { + customerId: 2868969472, + personaId: 20, + personaName: 'Molly', + shardId: 44, + }, + { + customerId: 5551212, // 0x54 0xB4 0x6C + personaId: 21, + personaName: 'Dr Brown', + shardId: 44, + }, + { + customerId: 0, + personaId: 22, + personaName: 'Admin', + shardId: 44, + }, ]; /** + * Retrieves all persona records associated with the specified customer ID. * - * @param {number} customerId -// * @return {Promise} + * @param customerId - The unique identifier of the customer whose personas are to be retrieved. + * @returns A promise that resolves to an array of persona records for the given customer. */ async function getPersonasByCustomerId( - customerId: number, + customerId: number, ): Promise< - Pick[] + Pick< + PersonaRecord, + 'customerId' | 'personaId' | 'personaName' | 'shardId' + >[] > { - const results = personaRecords.filter( - (persona) => persona.customerId === customerId, - ); - return results; + const results = personaRecords.filter( + (persona) => persona.customerId === customerId, + ); + return results; } /** - * Lookup all personas owned by the customer id + * Retrieves all persona records associated with the specified customer ID. * - * TODO: Store in a database, instead of being hard-coded + * Returns an array of persona records for the given {@link customerId}, or an empty array if none are found. * - * @param {number} customerId - * @return {Promise} + * @param customerId - The unique identifier of the customer whose personas are to be retrieved. + * @returns A promise resolving to an array of persona records for the customer. + * + * @remark Only customer ID 5551212 is currently supported; all other IDs return an empty array. */ async function getPersonaMapsByCustomerId( - customerId: number, + customerId: number, ): Promise< - Pick[] + Pick< + PersonaRecord, + 'customerId' | 'personaId' | 'personaName' | 'shardId' + >[] > { - switch (customerId) { - case 5551212: - return getPersonasByCustomerId(customerId); - default: - return []; - } + switch (customerId) { + case 5551212: + return getPersonasByCustomerId(customerId); + default: + return []; + } } /** - * Handle a get persona maps packet - * @param {object} args - * @param {string} args.connectionId - * @param {LegacyMessage} args.message - * @param {ServerLogger} [args.log=getServerLogger({ name: "LoginServer" })] - * @returns {Promise<{ - * connectionId: string, - * messages: SerializedBufferOld[], - * }>} + * Processes a "Get persona maps" request and returns serialized persona data for a given customer. + * + * Extracts the customer ID from the incoming message, retrieves associated persona records, serializes them into a response message, and returns the result for network transmission. + * + * @param connectionId - The identifier for the client connection. + * @param message - The incoming legacy message containing the request data. + * @returns An object containing the connection ID and an array with the serialized persona maps message. + * + * @throws {Error} If serialization of the persona maps message fails. */ async function getPersonaMaps({ - connectionId, - message, - log = getServerLogger("PersonaServer/_getPersonaMaps"), + connectionId, + message, + log = getServerLogger('PersonaServer/_getPersonaMaps'), }: { - connectionId: string; - message: LegacyMessage; - log?: ServerLogger; + connectionId: string; + message: LegacyMessage; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - log.debug("_npsGetPersonaMaps..."); + log.debug('_npsGetPersonaMaps...'); - const requestPacket = message; - log.debug( - `NPSMsg request object from _npsGetPersonaMaps ${requestPacket - ._doSerialize() - .toString("hex")} `, - ); + const requestPacket = message; + log.debug( + `NPSMsg request object from _npsGetPersonaMaps ${requestPacket + ._doSerialize() + .toString('hex')} `, + ); - const customerId = requestPacket.data.readUInt32BE(8); + const customerId = requestPacket.data.readUInt32BE(8); - const personas = await getPersonaMapsByCustomerId(customerId); - log.debug(`${personas.length} personas found for ${customerId}`); + const personas = await getPersonaMapsByCustomerId(customerId); + log.debug(`${personas.length} personas found for ${customerId}`); - const personaMapsMessage = new PersonaMapsMessage(); + const personaMapsMessage = new PersonaMapsMessage(); - // this is a GLDP_PersonaList::GLDP_PersonaList + // this is a GLDP_PersonaList::GLDP_PersonaList - try { - /** @type {PersonaList} */ - let personaList: PersonaList = new PersonaList(); + try { + /** @type {PersonaList} */ + let personaList: PersonaList = new PersonaList(); - if (personas.length > 1) { - log.warn(`More than one persona found for customer Id: ${customerId}`); - } + if (personas.length > 1) { + log.warn( + `More than one persona found for customer Id: ${customerId}`, + ); + } - personas.forEach((persona) => { - const personaRecord = new PersonaRecord(); + personas.forEach((persona) => { + const personaRecord = new PersonaRecord(); - personaRecord.customerId = persona.customerId; - personaRecord.personaId = persona.personaId; - personaRecord.personaName = persona.personaName; - personaRecord.shardId = persona.shardId; - personaRecord.numberOfGames = personas.length; + personaRecord.customerId = persona.customerId; + personaRecord.personaId = persona.personaId; + personaRecord.personaName = persona.personaName; + personaRecord.shardId = persona.shardId; + personaRecord.numberOfGames = personas.length; - personaList.addPersonaRecord(personaRecord); + personaList.addPersonaRecord(personaRecord); - log.debug( - `Persona record: ${JSON.stringify({ - personaRecord: personaRecord.toJSON(), - })}`, - ); - }); + log.debug( + `Persona record: ${JSON.stringify({ + personaRecord: personaRecord.toJSON(), + })}`, + ); + }); - personaMapsMessage._header.id = 0x607; - personaMapsMessage._personaRecords = personaList; - personaMapsMessage.setBuffer(personaList.serialize()); - log.debug( - `PersonaMapsMessage object from _npsGetPersonaMaps', + personaMapsMessage.header.setMessageId(0x607); + personaMapsMessage._personaRecords = personaList; + personaMapsMessage.setBody(personaList.serialize()); + log.debug( + `PersonaMapsMessage object from _npsGetPersonaMaps', ${JSON.stringify({ - personaMapsMessage: personaMapsMessage - .serialize() - .toString("hex"), - })}`, - ); + personaMapsMessage: personaMapsMessage + .serialize() + .toString('hex'), + })}`, + ); - const outboundMessage = new SerializedBufferOld(); - outboundMessage._doDeserialize(personaMapsMessage.serialize()); + const outboundMessage = new SerializedBufferOld(); + outboundMessage._doDeserialize(personaMapsMessage.serialize()); - return { - connectionId, - messages: [outboundMessage], - }; - } catch (error) { - const err = Error(`Error serializing personaMapsMsg`); - err.cause = error; - throw err; - } + return { + connectionId, + messages: [outboundMessage], + }; + } catch (error) { + const err = Error('Error serializing personaMapsMsg'); + err.cause = error; + throw err; + } } +/** + * Returns a formatted string representation of a persona record. + * + * @param persona - Partial persona record to format. + * @returns A string displaying the {@link persona}'s customerId, personaId, personaName, and shardId. + */ export function personaToString(persona: Partial): string { - return "".concat( - `PersonaRecord: customerId=${persona.customerId}, `, - `personaId=${persona.personaId}, `, - `name=${persona.personaName}, `, - `shardId=${persona.shardId}`, - ); + return ''.concat( + `PersonaRecord: customerId=${persona.customerId}, `, + `personaId=${persona.personaId}, `, + `name=${persona.personaName}, `, + `shardId=${persona.shardId}`, + ); } diff --git a/packages/persona/src/receivePersonaData.ts b/packages/persona/src/receivePersonaData.ts index 0125400b9..e6129b2ba 100644 --- a/packages/persona/src/receivePersonaData.ts +++ b/packages/persona/src/receivePersonaData.ts @@ -1,11 +1,7 @@ -import { - getServerLogger, - ServerLogger, - SerializedBufferOld, - LegacyMessage, -} from "rusty-motors-shared"; -import type { BufferSerializer } from "rusty-motors-shared-packets"; -import { messageHandlers } from "./internal"; +import { SerializedBufferOld, LegacyMessage } from 'rusty-motors-shared'; +import type { BufferSerializer } from 'rusty-motors-shared-packets'; +import { messageHandlers } from './internal.js'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; /** * @@ -22,50 +18,52 @@ import { messageHandlers } from "./internal"; */ export async function receivePersonaData({ - connectionId, - message, - log = getServerLogger("PersonaServer/receivePersonaData"), + connectionId, + message, + log = getServerLogger('PersonaServer/receivePersonaData'), }: { - connectionId: string; - message: BufferSerializer; - log?: ServerLogger; + connectionId: string; + message: BufferSerializer; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - const data = message.serialize(); - log.debug(`[${connectionId}] Entering receivePersonaData`); - log.debug(`[${connectionId}] Received persona data: ${data.toString("hex")}`); + const data = message.serialize(); + log.debug(`[${connectionId}] Entering receivePersonaData`); + log.debug( + `[${connectionId}] Received persona data: ${data.toString('hex')}`, + ); - // The packet needs to be an NPSMessage - const inboundMessage = new LegacyMessage(); - inboundMessage._doDeserialize(message.serialize()); + // The packet needs to be an NPSMessage + const inboundMessage = new LegacyMessage(); + inboundMessage._doDeserialize(message.serialize()); - const supportedHandler = messageHandlers.find((h) => { - return h.opCode === inboundMessage._header.id; - }); + const supportedHandler = messageHandlers.find((h) => { + return h.opCode === inboundMessage._header.id; + }); - if (typeof supportedHandler === "undefined") { - // We do not yet support this message code - throw Error( - `[${connectionId}] UNSUPPORTED_MESSAGECODE: ${inboundMessage._header.id}`, - ); - } + if (typeof supportedHandler === 'undefined') { + // We do not yet support this message code + throw Error( + `[${connectionId}] UNSUPPORTED_MESSAGECODE: ${inboundMessage._header.id}`, + ); + } - try { - const result = await supportedHandler.handler({ - connectionId, - message: inboundMessage, - log, - }); - log.debug( - `[${connectionId}] Returning with ${result.messages.length} messages`, - ); - return result; - } catch (error) { - const err = Error(`[${connectionId}] Error handling persona data`, { - cause: error, - }); - throw err; - } + try { + const result = await supportedHandler.handler({ + connectionId, + message: inboundMessage, + log, + }); + log.debug( + `[${connectionId}] Returning with ${result.messages.length} messages`, + ); + return result; + } catch (error) { + const err = Error(`[${connectionId}] Error handling persona data`, { + cause: error, + }); + throw err; + } } diff --git a/packages/shared/config/custom-environment-variables.json b/packages/shared/config/custom-environment-variables.json new file mode 100644 index 000000000..5d2dbd98c --- /dev/null +++ b/packages/shared/config/custom-environment-variables.json @@ -0,0 +1,7 @@ +{ + "host": "EXTERNAL_HOST", + "logLevel": "MCO_LOG_LEVEL", + "certificateFile": "CERTIFICATE_FILE", + "privateKeyFile": "PRIVATE_KEY_FILE", + "publicKeyFile": "PUBLIC_KEY_FILE" +} diff --git a/packages/shared/config/default.json b/packages/shared/config/default.json new file mode 100644 index 000000000..7d387f748 --- /dev/null +++ b/packages/shared/config/default.json @@ -0,0 +1,7 @@ +{ + "host": "localhost", + "logLevel": "debug", + "certificateFile": "./data/mcouniverse.crt", + "privateKeyFile": "./data/private_key.pem", + "publicKeyFile": "./data/pub.key" +} diff --git a/packages/shared/index.ts b/packages/shared/index.ts index 0eb521bfa..660b500b9 100644 --- a/packages/shared/index.ts +++ b/packages/shared/index.ts @@ -1,186 +1,54 @@ -export { SubThread } from "./src/SubThread.js"; -export { NetworkMessage } from "./src/NetworkMessage.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export { SubThread } from './src/SubThread.js'; +export { NetworkMessage } from './src/NetworkMessage.js'; +export { Configuration, getServerConfiguration } from './src/Configuration.js'; +export { SerializedBuffer } from './src/SerializedBuffer.js'; +export { SerializedBufferOld } from './src/SerializedBufferOld.js'; +export { RawMessage } from './src/RawMessage.js'; +export { ServerMessage } from './src/ServerMessage.js'; export { - Configuration, - getServerConfiguration, -} from "./src/Configuration.js"; -export { SerializedBuffer } from "./src/SerializedBuffer.js"; -export { SerializedBufferOld } from "./src/SerializedBufferOld.js"; -export { RawMessage } from "./src/RawMessage.js"; -export { ServerMessage } from "./src/ServerMessage.js"; + AbstractSerializable, + SerializableMixin, +} from './src/messageFactory.js'; +export { NPSMessage } from './src/NPSMessage.js'; +export { OldServerMessage } from './src/OldServerMessage.js'; +export { MessageBufferOld } from './src/MessageBufferOld.js'; +export { GameMessage } from './src/GameMessage.js'; +export { serializeString } from './src/serializeString.js'; +export { deserializeString } from './src/deserializeString.js'; +export { serializeStringRaw } from './src/serializeStringRaw.js'; +export { MessageNode } from './src/MessageNode.js'; +export { Timestamp } from './src/TimeStamp.js'; export { - AbstractSerializable, - SerializableMixin, -} from "./src/messageFactory.js"; -export { NPSMessage } from "./src/NPSMessage.js"; -export { OldServerMessage } from "./src/OldServerMessage.js"; -export { MessageBufferOld } from "./src/MessageBufferOld.js"; -export { GameMessage } from "./src/GameMessage.js"; -export { serializeString } from "./src/serializeString.js"; -export { deserializeString } from "./src/deserializeString.js"; -export { serializeStringRaw } from "./src/serializeStringRaw.js"; -export { MessageNode } from "./src/MessageNode.js"; -export { Timestamp } from "./src/TimeStamp.js"; -export { - McosEncryptionPair, - McosEncryption, - addSession, - createInitialState, - fetchStateFromDatabase, - addEncryption, - getEncryption, - McosSession, - findSessionByConnectionId, - updateEncryption, -} from "./src/State.js"; -export { ensureLegacyCipherCompatibility as verifyLegacyCipherSupport } from "./src/verifyLegacyCipherSupport.js"; -export type { State } from "./src/State.js"; -export type { OnDataHandler, ServiceResponse } from "./src/State.js"; -export { LegacyMessage } from "./src/LegacyMessage.js"; -export { NPSHeader } from "./src/NPSHeader.js"; -export * from "./src/interfaces.js"; -import * as Sentry from "@sentry/node"; -import pino from "pino"; - -export interface KeypressEvent { - sequence: string; - name: string; - ctrl: boolean; - meta: boolean; - shift: boolean; -} - -export interface ConnectionRecord { - customerId: number; - connectionId: string; - sessionKey: string; - sKey: string; - contextId: string; -} - -// Function to convert ARGB to 32-bit integer -export function argbToInt( - alpha: number, - red: number, - green: number, - blue: number, -) { - return ( - ((alpha & 0xff) << 24) | - ((red & 0xff) << 16) | - ((green & 0xff) << 8) | - (blue & 0xff) - ); -} - -// Function to convert 32-bit integer to ARGB -export function intToArgb(int: number) { - return { - alpha: (int >> 24) & 0xff, - red: (int >> 16) & 0xff, - green: (int >> 8) & 0xff, - blue: int & 0xff, - }; -} - -//skin colors -export const skin_pale = argbToInt(255, 255, 206, 165); //light pale -export const skin_tan = argbToInt(255, 206, 164, 122); //light tan -export const skin_brown = argbToInt(255, 112, 95, 78); //light brown -//shaded versions of the basic skin colors -export const dskin_pale = argbToInt(255, 140, 115, 90); //dark pale -export const dskin_tan = argbToInt(255, 124, 98, 72); //dark tan -export const dskin_brown = argbToInt(255, 63, 49, 35); //dark brown -//hair colors -export const hair_white = argbToInt(255, 255, 255, 255); //white -export const hair_platinum = argbToInt(255, 255, 242, 167); //platinum blonde -export const hair_blonde = argbToInt(255, 244, 219, 76); //blonde -export const hair_tan = argbToInt(255, 122, 100, 49); //tan -export const hair_red = argbToInt(255, 172, 69, 13); //red -export const hair_brown = argbToInt(255, 81, 65, 29); //brown -export const hair_black = argbToInt(255, 0, 0, 0); //black -//clothing colors -export const cloth_red = argbToInt(255, 212, 82, 82); //red -export const cloth_orange = argbToInt(255, 229, 139, 38); //orange -export const cloth_yellow = argbToInt(255, 255, 216, 0); //yellow -export const cloth_green = argbToInt(255, 112, 158, 113); //green -export const cloth_blue = argbToInt(255, 67, 81, 168); //blue -export const cloth_purple = argbToInt(255, 121, 80, 132); //purple -export const cloth_brown = argbToInt(255, 117, 104, 68); //brown -export const cloth_black = argbToInt(255, 68, 68, 68); //black -export const cloth_grey = argbToInt(255, 146, 143, 137); //grey -export const cloth_white = argbToInt(255, 255, 255, 255); //white - -interface Logger { - info: (msg: string, obj?: unknown) => void; - warn: (msg: string, obj?: unknown) => void; - error: (msg: string, obj?: unknown) => void; - fatal: (msg: string, obj?: unknown) => void; - debug: (msg: string, obj?: unknown) => void; - trace: (msg: string, obj?: unknown) => void; - child: (obj: pino.Bindings) => Logger; -} - -type LogLevel = "fatal" | "error" | "warn" | "info" | "debug" | "trace"; - -let logger: pino.Logger; - -export function getServerLogger(name?: string): Logger { - if (logger) { - return logger.child({ name }); - } - const loggerName = name || "core"; - const validLogLevels = ['fatal', 'error', 'warn', 'info', 'debug', 'trace'] as const; - const logLevel = process.env["MCO_LOG_LEVEL"] || "debug"; - - if (!validLogLevels.includes(logLevel as LogLevel)) { - console.warn(`Invalid log level: ${logLevel}. Defaulting to "debug"`); - } - - logger = pino({ - name: loggerName, - transport: { - targets: [ - { - target: "pino-pretty", - options: { - colorize: true, - translateTime: "SYS:standard", - }, - level: logLevel, - }, - { - target: "pino/file", - options: { - destination: `./logs/server.log`, - mkdir: true, - append: false - }, - level: logLevel, - } - ], - }, - level: logLevel, - }); - - return { - info: logger.info.bind(logger), - warn: logger.warn.bind(logger), - error: (msg: string, obj?: unknown) => { - if (obj instanceof Error) { - Sentry.captureException(obj); - } else if (obj) { - Sentry.captureException(new Error(msg), { extra: { context: obj } }); - } else { - Sentry.captureException(new Error(msg)); - } - logger.error({ msg, obj }); - }, - fatal: logger.fatal.bind(logger), - debug: logger.debug.bind(logger), - trace: logger.trace.bind(logger), - child: (obj: pino.Bindings) => logger.child(obj), - } -} - -export type ServerLogger = Logger; \ No newline at end of file + McosEncryptionPair, + McosEncryption, + addSession, + createInitialState, + fetchStateFromDatabase, + addEncryption, + getEncryption, + McosSession, + findSessionByConnectionId, + updateEncryption, +} from './src/State.js'; +export { ensureLegacyCipherCompatibility as verifyLegacyCipherSupport } from './src/verifyLegacyCipherSupport.js'; +export type { State } from './src/State.js'; +export type { OnDataHandler, ServiceResponse } from './src/State.js'; +export { LegacyMessage } from './src/LegacyMessage.js'; +export * from './src/types.js'; +export * from './src/utility.js'; diff --git a/packages/shared/package.json b/packages/shared/package.json index 9218648d1..8c157a4d9 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -9,6 +9,10 @@ "./test": { "import": "./test/index.js", "require": "./test/index.js" + }, + "./logger.js": { + "import": "./getServerLogger.js", + "require": "./getServerLogger.js" } }, "type": "module", @@ -16,16 +20,19 @@ "check": "tsc", "lint": "npx @biomejs/biome lint --write .", "format": "npx @biomejs/biome format --write .", - "test": "vitest run --coverage", - "build": "tsc" + "test": "NODE_CONFIG_DIR=./config vitest run --coverage", + "build": "tsc" }, "keywords": [], "author": "", "license": "AGPL-3.0", "dependencies": { + "config": "^4.0.0", + "dotenv": "^16.5.0", "fastify": "^5.3.3", "pino": "^9.6.0", "pino-pretty": "^13.0.0", + "rusty-motors-logger": "workspace:^", "rusty-motors-shared-packets": "workspace:1.0.0-next.0" }, "directories": { @@ -33,6 +40,7 @@ }, "description": "", "devDependencies": { + "@types/config": "^3.3.5", "@vitest/coverage-v8": "3.1.3", "vitest": "^3.1.3" } diff --git a/packages/shared/src/BaseSerialized.ts b/packages/shared/src/BaseSerialized.ts index 59d8c3244..efd905ebd 100644 --- a/packages/shared/src/BaseSerialized.ts +++ b/packages/shared/src/BaseSerialized.ts @@ -1,10 +1,26 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + export interface Serializable { - data: Buffer; - serialize(): Buffer; - deserialize(buffer: Buffer): T; - length: number; - toString(): string; - asHex(): string; + data: Buffer; + serialize(): Buffer; + deserialize(buffer: Buffer): T; + length: number; + toString(): string; + asHex(): string; } /** @@ -12,37 +28,37 @@ export interface Serializable { * Just a wrapper around a buffer */ export class BaseSerialized implements Serializable { - protected _data: Buffer; + protected _data: Buffer; - constructor(data?: Buffer) { - this._data = data || Buffer.alloc(0); - } + constructor(data?: Buffer) { + this._data = data || Buffer.alloc(0); + } - get data(): Buffer { - return this._data; - } + get data(): Buffer { + return this._data; + } - set data(data: Buffer) { - this._data = Buffer.from(data); - } + set data(data: Buffer) { + this._data = Buffer.from(data); + } - serialize(): Buffer { - throw Error("Not implemented"); - } + serialize(): Buffer { + throw Error('Not implemented'); + } - deserialize(_buffer: Buffer): T { - throw Error("Not implemented"); - } + deserialize(_buffer: Buffer): T { + throw Error('Not implemented'); + } - get length(): number { - return this._data.length; - } + get length(): number { + return this._data.length; + } - toString(): string { - return this.asHex(); - } + toString(): string { + return this.asHex(); + } - asHex(): string { - return this._data.toString("hex"); - } + asHex(): string { + return this._data.toString('hex'); + } } diff --git a/packages/shared/src/Configuration.test.ts b/packages/shared/src/Configuration.test.ts index 1fa89f86e..2afb5888b 100644 --- a/packages/shared/src/Configuration.test.ts +++ b/packages/shared/src/Configuration.test.ts @@ -1,64 +1,40 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { getServerConfiguration } from "./Configuration"; -import { getServerLogger } from "../index.js"; - -vi.mock("../index.js", () => ({ - getServerLogger: vi.fn().mockReturnValue({ - fatal: vi.fn(), - }), -})); - -describe("getServerConfiguration", () => { - const OLD_ENV = process.env; - - beforeEach(() => { - vi.resetModules(); - process.env = { ...OLD_ENV }; - }); - - afterEach(() => { - process.env = OLD_ENV; - }); - - it("should return the correct configuration when all environment variables are set", () => { - process.env["EXTERNAL_HOST"] = "localhost"; - process.env["CERTIFICATE_FILE"] = "/path/to/cert"; - process.env["PRIVATE_KEY_FILE"] = "/path/to/privateKey"; - process.env["PUBLIC_KEY_FILE"] = "/path/to/publicKey"; - process.env["MCO_LOG_LEVEL"] = "info"; - - const config = getServerConfiguration(); - - expect(config.host).toBe("localhost"); - expect(config.certificateFile).toBe("/path/to/cert"); - expect(config.privateKeyFile).toBe("/path/to/privateKey"); - expect(config.publicKeyFile).toBe("/path/to/publicKey"); - expect(config.logLevel).toBe("info"); - }); - - it("should use default values for optional environment variables", () => { - process.env["CERTIFICATE_FILE"] = "/path/to/cert"; - process.env["PRIVATE_KEY_FILE"] = "/path/to/privateKey"; - process.env["PUBLIC_KEY_FILE"] = "/path/to/publicKey"; - - const config = getServerConfiguration(); - - expect(config.host).toBe(""); - expect(config.logLevel).toBe("debug"); - }); - - it("should exit the process if required environment variables are missing", () => { - const mockExit = vi.spyOn(process, "exit").mockImplementation(() => { - throw new Error("process.exit called"); - }); - const mockLogger = getServerLogger("core"); - - expect(() => getServerConfiguration()).toThrow("process.exit called"); - expect(mockLogger.fatal).toHaveBeenCalledWith( - "Missing required environment variable: CERTIFICATE_FILE", - ); - expect(mockExit).toHaveBeenCalledWith(1); - - mockExit.mockRestore(); - }); -}); \ No newline at end of file +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { getServerConfiguration } from './Configuration.js'; + +describe('getServerConfiguration', () => { + const OLD_ENV = process.env; + + beforeEach(() => { + vi.resetModules(); + process.env = { ...OLD_ENV }; + }); + + afterEach(() => { + process.env = OLD_ENV; + }); + + it('should return the correct configuration, using environment variables if set, otherwise defaults', () => { + // Unset all env vars + delete process.env['EXTERNAL_HOST']; + delete process.env['CERTIFICATE_FILE']; + delete process.env['PRIVATE_KEY_FILE']; + delete process.env['PUBLIC_KEY_FILE']; + delete process.env['MCO_LOG_LEVEL']; + // Should use defaults from config + let config = getServerConfiguration(); + expect(config.host).toBe('localhost'); + expect(config.certificateFile).toBe('./data/mcouniverse.crt'); + expect(config.privateKeyFile).toBe('./data/private_key.pem'); + expect(config.publicKeyFile).toBe('./data/pub.key'); + expect(config.logLevel).toBe('debug'); + + // Set env vars and reload config (requires new process, so just check that config system prioritizes env if set before process start) + process.env['EXTERNAL_HOST'] = 'localhost'; + process.env['CERTIFICATE_FILE'] = '/path/to/cert'; + process.env['PRIVATE_KEY_FILE'] = '/path/to/privateKey'; + process.env['PUBLIC_KEY_FILE'] = '/path/to/publicKey'; + process.env['MCO_LOG_LEVEL'] = 'info'; + // Note: config package only reads env vars at process start, so this will not override in the same process. + // This is a limitation of the config package and cannot be tested in a single process. + }); +}); diff --git a/packages/shared/src/Configuration.ts b/packages/shared/src/Configuration.ts index 3b395f763..98266c161 100644 --- a/packages/shared/src/Configuration.ts +++ b/packages/shared/src/Configuration.ts @@ -1,5 +1,22 @@ -import type { Logger } from "pino"; -import { getServerLogger } from "../index.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { getServerLogger } from 'rusty-motors-logger'; +import config from 'config'; +import 'dotenv/config'; /** * @module shared/Configuration @@ -7,128 +24,116 @@ import { getServerLogger } from "../index.js"; */ export class Configuration { - certificateFile!: string; - privateKeyFile!: string; - publicKeyFile!: string; - host!: string; - logLevel!: string; - static instance: Configuration | undefined; - - /** - * Constructs a new Configuration instance. - * - * @param {Object} params - The configuration parameters. - * @param {string} params.host - The host address. - * @param {string} params.certificateFile - The path to the certificate file. - * @param {string} params.privateKeyFile - The path to the private key file. - * @param {string} params.publicKeyFile - The path to the public key file. - * @param {string} params.logLevel - The logging level. - * @param {Logger} params.logger - The logger instance. - */ - constructor({ - host, - certificateFile, - privateKeyFile, - publicKeyFile, - logLevel, - logger, - }: { - host: string; - certificateFile: string; - privateKeyFile: string; - publicKeyFile: string; - logLevel: string; - logger: Logger; - }) { - try { - this.certificateFile = certificateFile; + certificateFile!: string; + privateKeyFile!: string; + publicKeyFile!: string; + host!: string; + logLevel!: string; + static instance: Configuration | undefined; - this.privateKeyFile = privateKeyFile; + /** + * Constructs a new Configuration instance. + * + * @param {Object} params - The configuration parameters. + * @param {string} params.host - The host address. + * @param {string} params.certificateFile - The path to the certificate file. + * @param {string} params.privateKeyFile - The path to the private key file. + * @param {string} params.publicKeyFile - The path to the public key file. + * @param {string} params.logLevel - The logging level. + * @param {Logger} params.logger - The logger instance. + */ + constructor({ + host, + certificateFile, + privateKeyFile, + publicKeyFile, + logLevel, + logger, + }: { + host: string; + certificateFile: string; + privateKeyFile: string; + publicKeyFile: string; + logLevel: string; + logger: any; // Relaxed type to avoid pino vs custom logger conflict + }) { + try { + this.certificateFile = certificateFile; - this.publicKeyFile = publicKeyFile; + this.privateKeyFile = privateKeyFile; - this.host = host; + this.publicKeyFile = publicKeyFile; - this.logLevel = logLevel.toLowerCase(); - Configuration.instance = this; - } catch (error) { - logger.fatal(`Error in core server: ${String(error)}`); - } - } + this.host = host; - /** - * Creates a new instance of the Configuration class. - * - * @param host - The host address. - * @param certificateFile - The path to the certificate file. - * @param privateKeyFile - The path to the private key file. - * @param publicKeyFile - The path to the public key file. - * @param logLevel - The logging level. - * @param logger - The logger instance. - * @returns A new Configuration instance. - */ - static newInstance({ - host, - certificateFile, - privateKeyFile, - publicKeyFile, - logLevel, - logger, - }: { - host: string; - certificateFile: string; - privateKeyFile: string; - publicKeyFile: string; - logLevel: string; - logger: Logger; - }): Configuration { - return new Configuration({ - host, - certificateFile, - privateKeyFile, - publicKeyFile, - logLevel, - logger, - }); - } + this.logLevel = logLevel.toLowerCase(); + Configuration.instance = this; + } catch (error) { + logger.fatal(`Error in core server: ${String(error)}`); + } + } - /** - * Returns the singleton instance of the Configuration class. - * - * @throws {Error} If the Configuration instance has not been initialized using newInstance. - * @returns {Configuration} The singleton instance of the Configuration class. - */ - static getInstance(): Configuration { - if (typeof Configuration.instance === "undefined") { - throw new Error( - "Configuration needs to be initialized using newInstance", - ); - } + /** + * Creates a new instance of the Configuration class. + * + * @param host - The host address. + * @param certificateFile - The path to the certificate file. + * @param privateKeyFile - The path to the private key file. + * @param publicKeyFile - The path to the public key file. + * @param logLevel - The logging level. + * @param logger - The logger instance. + * @returns A new Configuration instance. + */ + static newInstance({ + host, + certificateFile, + privateKeyFile, + publicKeyFile, + logLevel, + logger, + }: { + host: string; + certificateFile: string; + privateKeyFile: string; + publicKeyFile: string; + logLevel: string; + logger: any; // Relaxed type + }): Configuration { + return new Configuration({ + host, + certificateFile, + privateKeyFile, + publicKeyFile, + logLevel, + logger, + }); + } - return Configuration.instance; - } -} + /** + * Returns the singleton instance of the Configuration class. + * + * @throws {Error} If the Configuration instance has not been initialized using newInstance. + * @returns {Configuration} The singleton instance of the Configuration class. + */ + static getInstance(): Configuration { + if (typeof Configuration.instance === 'undefined') { + throw new Error( + 'Configuration needs to be initialized using newInstance', + ); + } -function getEnvVariable( - name: string, - required: boolean, - defaultValue?: string, -): string { - const value = process.env[name]; - if (required && !value) { - const coreLogger = getServerLogger("core"); - coreLogger.fatal(`Missing required environment variable: ${name}`); - process.exit(1); - } - return value || defaultValue || ""; + return Configuration.instance; + } } export function getServerConfiguration(): Configuration { - return { - host: getEnvVariable("EXTERNAL_HOST", false, ""), - certificateFile: getEnvVariable("CERTIFICATE_FILE", true), - privateKeyFile: getEnvVariable("PRIVATE_KEY_FILE", true), - publicKeyFile: getEnvVariable("PUBLIC_KEY_FILE", true), - logLevel: getEnvVariable("MCO_LOG_LEVEL", false, "debug"), - }; + const logger = getServerLogger('core'); + return new Configuration({ + host: config.get('host'), + certificateFile: config.get('certificateFile'), + privateKeyFile: config.get('privateKeyFile'), + publicKeyFile: config.get('publicKeyFile'), + logLevel: config.get('logLevel'), + logger, + }); } diff --git a/packages/shared/src/GameMessageHeader.ts b/packages/shared/src/GameMessageHeader.ts index 550deccb0..b5bb607ed 100644 --- a/packages/shared/src/GameMessageHeader.ts +++ b/packages/shared/src/GameMessageHeader.ts @@ -1,4 +1,4 @@ -import { legacyHeader } from "./legacyHeader.js"; +import { legacyHeader } from './legacyHeader.js'; /** * A game message header is a 8 byte header with the following fields: @@ -9,45 +9,49 @@ import { legacyHeader } from "./legacyHeader.js"; */ export class GameMessageHeader extends legacyHeader { - _gameMessageId: number; // 2 bytes - _gameMessageLength: number; // 2 bytes + _gameMessageId: number; // 2 bytes + _gameMessageLength: number; // 2 bytes - constructor(gameMessageId: number) { - super(); - this._size = 8; - this.id = 0x1101; // 2 bytes - this._gameMessageId = gameMessageId; // 2 bytes - this._gameMessageLength = 0; // 2 bytes - } + constructor(gameMessageId: number) { + super(); + this._size = 8; + this.id = 0x1101; // 2 bytes + this._gameMessageId = gameMessageId; // 2 bytes + this._gameMessageLength = 0; // 2 bytes + } - size() { - return 8; - } + size() { + return 8; + } - deserialize(buffer: Buffer) { - if (buffer.length < 8) { - throw Error(`Buffer length ${buffer.length} is too short to deserialize`); - } + override deserialize(buffer: Buffer) { + if (buffer.length < 8) { + throw Error( + `Buffer length ${buffer.length} is too short to deserialize`, + ); + } - try { - this.id = buffer.readInt16BE(0); - this.length = buffer.readInt16BE(2); - this._gameMessageId = buffer.readInt16BE(4); - this._gameMessageLength = buffer.readInt16BE(6); - } catch (error) { - const err = new Error(`Error deserializing buffer: ${String(error)}`); - err.cause = error; - throw err; - } - return this; - } + try { + this.id = buffer.readInt16BE(0); + this.length = buffer.readInt16BE(2); + this._gameMessageId = buffer.readInt16BE(4); + this._gameMessageLength = buffer.readInt16BE(6); + } catch (error) { + const err = new Error( + `Error deserializing buffer: ${String(error)}`, + ); + err.cause = error; + throw err; + } + return this; + } - serialize() { - const buffer = Buffer.alloc(8); - buffer.writeInt16BE(this.id, 0); - buffer.writeInt16BE(this.length, 2); - buffer.writeInt16BE(this._gameMessageId, 4); - buffer.writeInt16BE(this._gameMessageLength, 6); - return buffer; - } + override serialize() { + const buffer = Buffer.alloc(8); + buffer.writeInt16BE(this.id, 0); + buffer.writeInt16BE(this.length, 2); + buffer.writeInt16BE(this._gameMessageId, 4); + buffer.writeInt16BE(this._gameMessageLength, 6); + return buffer; + } } diff --git a/packages/shared/src/LegacyMessage.ts b/packages/shared/src/LegacyMessage.ts index 01d1b49d6..b65c432dd 100644 --- a/packages/shared/src/LegacyMessage.ts +++ b/packages/shared/src/LegacyMessage.ts @@ -1,5 +1,5 @@ -import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; -import { legacyHeader } from "./legacyHeader.js"; +import { SerializableMixin, AbstractSerializable } from './messageFactory.js'; +import { legacyHeader } from './legacyHeader.js'; /** * A legacy message is an older nps message type. It has a 4 byte header. @see {@link legacyHeader} @@ -8,81 +8,81 @@ import { legacyHeader } from "./legacyHeader.js"; */ export class LegacyMessage extends SerializableMixin(AbstractSerializable) { - _header: legacyHeader; - constructor() { - super(); - this._header = new legacyHeader(); - } + _header: legacyHeader; + constructor() { + super(); + this._header = new legacyHeader(); + } - /** - * @deprecated Use `deserialize` instead - */ - override _doDeserialize(buffer: Buffer): LegacyMessage { - this._header._doDeserialize(buffer); - this.setBuffer(buffer.subarray(this._header._size)); - return this; - } + /** + * @deprecated Use `deserialize` instead + */ + override _doDeserialize(buffer: Buffer): LegacyMessage { + this._header._doDeserialize(buffer); + this.setBuffer(buffer.subarray(this._header._size)); + return this; + } - getMessageId() { - return this._header.id; - } + getMessageId() { + return this._header.id; + } - setMessageId(id: number) { - this._header.id = id; - } + setMessageId(id: number) { + this._header.id = id; + } - /** - * Deserializes the given buffer and updates the current instance with the deserialized data. - * - * @param buffer - The buffer containing the serialized data. - * @returns The current instance with the deserialized data. - */ - deserialize(buffer: Buffer) { - this._header._doDeserialize(buffer); - this.setBuffer(buffer.subarray(this._header._size)); - return this; - } + /** + * Deserializes the given buffer and updates the current instance with the deserialized data. + * + * @param buffer - The buffer containing the serialized data. + * @returns The current instance with the deserialized data. + */ + deserialize(buffer: Buffer) { + this._header._doDeserialize(buffer); + this.setBuffer(buffer.subarray(this._header._size)); + return this; + } - /** - * @deprecated Use serialize instead - */ - override _doSerialize() { - const buffer = Buffer.alloc(this._header.length); - this._header._doSerialize().copy(buffer); - super.data.copy(buffer, this._header._size); - return buffer; - } + /** + * @deprecated Use serialize instead + */ + override _doSerialize() { + const buffer = Buffer.alloc(this._header.length); + this._header._doSerialize().copy(buffer); + super.data.copy(buffer, this._header._size); + return buffer; + } - serialize() { - const buffer = Buffer.alloc(this._header.length); - this._header._doSerialize().copy(buffer); - super.data.copy(buffer, this._header._size); - return buffer; - } + serialize() { + const buffer = Buffer.alloc(this._header.length); + this._header._doSerialize().copy(buffer); + super.data.copy(buffer, this._header._size); + return buffer; + } - /** - * @param {Buffer} buffer - */ - override setBuffer(buffer: Buffer) { - super.setBuffer(buffer); - this._header.length = buffer.length + 4; - } + /** + * @param {Buffer} buffer + */ + override setBuffer(buffer: Buffer) { + super.setBuffer(buffer); + this._header.length = buffer.length + 4; + } - asJSON() { - return { - header: this._header, - data: super.data.toString("hex"), - }; - } + asJSON() { + return { + header: this._header, + data: super.data.toString('hex'), + }; + } - override toString() { - return `LegacyMessage: ${JSON.stringify({ - header: this._header.toString(), - data: super.data.toString("hex"), - })}`; - } + override toString() { + return `LegacyMessage: ${JSON.stringify({ + header: this._header.toString(), + data: super.data.toString('hex'), + })}`; + } - toHexString() { - return this.serialize().toString("hex"); - } + toHexString() { + return this.serialize().toString('hex'); + } } diff --git a/packages/shared/src/MessageBufferOld.ts b/packages/shared/src/MessageBufferOld.ts index eb0ee8154..2f91bb625 100644 --- a/packages/shared/src/MessageBufferOld.ts +++ b/packages/shared/src/MessageBufferOld.ts @@ -1,125 +1,143 @@ -import { MessageHeader } from "./MessageHeader.js"; -import { SerializedBufferOld } from "./SerializedBufferOld.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { MessageHeader } from './MessageHeader.js'; +import { SerializedBufferOld } from './SerializedBufferOld.js'; export class MessageBufferOld extends SerializedBufferOld { - _header: MessageHeader; - _buffer: Buffer; - constructor() { - super(); - this._header = new MessageHeader(); - this._buffer = Buffer.alloc(4); - } - - /** - * @param {number} id - The ID of the message - * @param {Buffer} buffer - The buffer to deserialize - * @returns {MessageBufferOld} - */ - static createGameMessage(id: number, buffer: Buffer): MessageBufferOld { - const message = new MessageBufferOld(); - message._header._messageId = id; - message.buffer = buffer; - return message; - } - - get messageId() { - return this._header.messageId; - } - - get messageLength() { - return this._header.messageLength; - } - - override get data() { - return this._buffer; - } - - override set data(buffer) { - this.buffer = buffer; - } - - /** - * @param {Buffer} buffer - */ - set buffer(buffer: Buffer) { - // const log = getServerLogger({ name: "MessageBuffer" }); - // log.level = getServerConfiguration({}).logLevel ?? "info"; - this._buffer = Buffer.alloc(buffer.length); - this._buffer = buffer; - this._header._messageLength = 4 + buffer.length; - // log.debug(`Message length: ${this._header._messageLength}`); - // log.debug(`Buffer length: ${this._buffer.length}`); - // Pad the buffer to a multiple of 8 bytes - // let extraBytes = 0; - // const x = (this._buffer.length + 4) % 8; - // extraBytes = 8 - x; - // log.debug(`Extra bytes: ${extraBytes}`); - // if (extraBytes !== 0) { - // this._buffer = Buffer.concat([ - // this._buffer, - // Buffer.alloc(extraBytes), - // ]); - // log.debug(`Buffer length: ${this._buffer.length}`); - this._header._messageLength = this._buffer.length + 4; - // log.debug(`Message length: ${this._header._messageLength}`); - // } - } - - /** @param {Buffer} buffer */ - override setBuffer(buffer: Buffer) { - this.buffer = buffer; - - return this._buffer; - } - - get buffer() { - return this._buffer; - } - - serializeSizeOf() { - return 4 + this._buffer.length; - } - - /** - * @param {Buffer} buffer - * @returns {MessageBufferOld} - */ - override deserialize(buffer: Buffer): MessageBufferOld { - this._header.deserialize(buffer.subarray(0, 8)); - if (buffer.length < 4 + this._header.messageLength) { - throw Error(`Buffer length ${buffer.length} is too short to deserialize`); - } - this.buffer = buffer.subarray(4); - return this; - } - - override serialize() { - const buffer = Buffer.alloc(4 + this._buffer.length); - if (this.buffer.length < 0) { - throw Error( - `Buffer length ${this.buffer.length} is too short to serialize`, - ); - } - if (this.messageId <= 0) { - throw Error(`Message ID ${this.messageId} is invalid to serialize`); - } - this._header.serialize().copy(buffer); - this._buffer.copy(buffer, 4); - return buffer; - } - - override toString() { - return `MessageBuffer: ${JSON.stringify({ - header: this._header.toString(), - bufferLength: this._buffer.length, - buffer: this._buffer.toString("hex"), - })}`; - } - - asJSON() { - return { - header: this._header, - buffer: this._buffer.toString("hex"), - }; - } + _header: MessageHeader; + _buffer: Buffer; + constructor() { + super(); + this._header = new MessageHeader(); + this._buffer = Buffer.alloc(4); + } + + /** + * @param {number} id - The ID of the message + * @param {Buffer} buffer - The buffer to deserialize + * @returns {MessageBufferOld} + */ + static createGameMessage(id: number, buffer: Buffer): MessageBufferOld { + const message = new MessageBufferOld(); + message._header._messageId = id; + message.buffer = buffer; + return message; + } + + get messageId() { + return this._header.messageId; + } + + get messageLength() { + return this._header.messageLength; + } + + override get data() { + return this._buffer; + } + + override set data(buffer) { + this.buffer = buffer; + } + + /** + * @param {Buffer} buffer + */ + set buffer(buffer: Buffer) { + // const log = getServerLogger({ name: "MessageBuffer" }); + // log.level = getServerConfiguration({}).logLevel ?? "info"; + this._buffer = Buffer.alloc(buffer.length); + this._buffer = buffer; + this._header._messageLength = 4 + buffer.length; + // log.debug(`Message length: ${this._header._messageLength}`); + // log.debug(`Buffer length: ${this._buffer.length}`); + // Pad the buffer to a multiple of 8 bytes + // let extraBytes = 0; + // const x = (this._buffer.length + 4) % 8; + // extraBytes = 8 - x; + // log.debug(`Extra bytes: ${extraBytes}`); + // if (extraBytes !== 0) { + // this._buffer = Buffer.concat([ + // this._buffer, + // Buffer.alloc(extraBytes), + // ]); + // log.debug(`Buffer length: ${this._buffer.length}`); + this._header._messageLength = this._buffer.length + 4; + // log.debug(`Message length: ${this._header._messageLength}`); + // } + } + + /** @param {Buffer} buffer */ + override setBuffer(buffer: Buffer) { + this.buffer = buffer; + + return this._buffer; + } + + get buffer() { + return this._buffer; + } + + serializeSizeOf() { + return 4 + this._buffer.length; + } + + /** + * @param {Buffer} buffer + * @returns {MessageBufferOld} + */ + override deserialize(buffer: Buffer): MessageBufferOld { + this._header.deserialize(buffer.subarray(0, 8)); + if (buffer.length < 4 + this._header.messageLength) { + throw Error( + `Buffer length ${buffer.length} is too short to deserialize`, + ); + } + this.buffer = buffer.subarray(4); + return this; + } + + override serialize() { + const buffer = Buffer.alloc(4 + this._buffer.length); + if (this.buffer.length < 0) { + throw Error( + `Buffer length ${this.buffer.length} is too short to serialize`, + ); + } + if (this.messageId <= 0) { + throw Error(`Message ID ${this.messageId} is invalid to serialize`); + } + this._header.serialize().copy(buffer); + this._buffer.copy(buffer, 4); + return buffer; + } + + override toString() { + return `MessageBuffer: ${JSON.stringify({ + header: this._header.toString(), + bufferLength: this._buffer.length, + buffer: this._buffer.toString('hex'), + })}`; + } + + asJSON() { + return { + header: this._header, + buffer: this._buffer.toString('hex'), + }; + } } diff --git a/packages/shared/src/NPSHeader.ts b/packages/shared/src/NPSHeader.ts deleted file mode 100644 index f13ad751e..000000000 --- a/packages/shared/src/NPSHeader.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; - -/** - * A nps header is a 12 byte header with the following fields: - * - 2 bytes - id - * - 2 bytes - length - * - 2 bytes - version - * - 2 bytes - reserved - * - 4 bytes - checksum - * - * @mixin {SerializableMixin} - */ - -export class NPSHeader extends SerializableMixin(AbstractSerializable) { - _size: number; - id: number; // 2 bytes - length: number; // 2 bytes - version: number; // 2 bytes - reserved: number; // 2 bytes - checksum: number; // 4 bytes - constructor() { - super(); - this._size = 12; - this.id = 0; // 2 bytes - this.length = this._size; // 2 bytes - this.version = 257; // 2 bytes (0x0101) - this.reserved = 0; // 2 bytes - this.checksum = 0; // 4 bytes - } - - /** - * @param {Buffer} buffer - * @returns {NPSHeader} - * @throws {Error} If the buffer is too short - * @throws {Error} If the buffer is malformed - */ - override _doDeserialize(buffer: Buffer): NPSHeader { - if (buffer.length < this._size) { - throw Error(`Buffer length ${buffer.length} is too short to deserialize`); - } - - try { - this.id = buffer.readInt16BE(0); - this.length = buffer.readInt16BE(2); - } catch (error) { - throw Error(`Error deserializing buffer: ${String(error)}`); - } - return this; - } - - override _doSerialize() { - const buffer = Buffer.alloc(this._size); - buffer.writeInt16BE(this.id, 0); - buffer.writeInt16BE(this.length, 2); - buffer.writeInt16BE(this.version, 4); - buffer.writeInt16BE(this.reserved, 6); - buffer.writeInt32BE(this.checksum, 8); - return buffer; - } - - static size() { - return 12; - } - - static override get Size() { - return 12; - } - - override toString() { - return `NPSHeader: ${JSON.stringify({ - id: this.id, - length: this.length, - version: this.version, - reserved: this.reserved, - checksum: this.checksum, - })}`; - } -} diff --git a/packages/shared/src/NPSMessage.ts b/packages/shared/src/NPSMessage.ts index edf1e9a7a..6b080e735 100644 --- a/packages/shared/src/NPSMessage.ts +++ b/packages/shared/src/NPSMessage.ts @@ -1,5 +1,20 @@ -import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; -import { NPSHeader } from "./NPSHeader.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { BytableHeader, BytableMessage } from '@rustymotors/binary'; // Ensure this path is correct /** * A NPS message is a message that matches version 1.1 of the nps protocol. It has a 12 byte header. @see {@link NPSHeader} @@ -7,38 +22,34 @@ import { NPSHeader } from "./NPSHeader.js"; * @mixin {SerializableMixin} */ -export class NPSMessage extends SerializableMixin(AbstractSerializable) { - _header: NPSHeader; - constructor() { - super(); - this._header = new NPSHeader(); - } +export class NPSMessage extends BytableMessage { + _header: BytableHeader; + constructor() { + super(); + this._header = new BytableHeader(); + } - /** - * @param {Buffer} buffer - * @returns {NPSMessage} - */ - override _doDeserialize(buffer: Buffer): NPSMessage { - this._header._doDeserialize(buffer); - this.setBuffer(buffer.subarray(this._header._size)); - return this; - } + override deserialize(buffer: Buffer) { + this._header.deserialize(buffer); + this.setBody(buffer.subarray(this._header.serializeSize)); + return this; + } - serialize() { - const buffer = Buffer.alloc(this._header.length); - this._header._doSerialize().copy(buffer); - this.data.copy(buffer, this._header._size); - return buffer; - } + override serialize() { + const buffer = Buffer.alloc(this._header.messageLength); + this._header.serialize().copy(buffer); + this.data.copy(buffer, this._header.serializeSize); + return buffer; + } - size() { - return this._header.length + this.data.length; - } + size() { + return this._header.serializeSize + this.data.length; + } - override toString() { - return `NPSMessage: ${JSON.stringify({ - header: this._header.toString(), - data: this.data.toString("hex"), - })}`; - } + override toString() { + return `NPSMessage: ${JSON.stringify({ + header: this._header.toString(), + data: this.data.toString('hex'), + })}`; + } } diff --git a/packages/shared/src/OldServerMessage.ts b/packages/shared/src/OldServerMessage.ts index b2fecfef2..235e2c87e 100644 --- a/packages/shared/src/OldServerMessage.ts +++ b/packages/shared/src/OldServerMessage.ts @@ -1,8 +1,22 @@ -import { IServerMessage } from "rusty-motors-shared-packets"; -import { SerializedBufferOld } from "./SerializedBufferOld.js"; -import { serverHeader } from "./serverHeader.js"; - +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +import { IServerMessage } from 'rusty-motors-shared-packets'; +import { SerializedBufferOld } from './SerializedBufferOld.js'; +import { serverHeader } from './serverHeader.js'; /** * A server message is a message that is passed between the server and the client. It has an 11 byte header. @see {@link serverHeader} @@ -10,76 +24,79 @@ import { serverHeader } from "./serverHeader.js"; * @mixin {SerializableMixin} * @deprecated */ -export class OldServerMessage extends SerializedBufferOld implements IServerMessage { - _header: serverHeader; - _msgNo: number; - constructor() { - super(); - this._header = new serverHeader(); - this._msgNo = 0; // 2 bytes - } +export class OldServerMessage + extends SerializedBufferOld + implements IServerMessage +{ + _header: serverHeader; + _msgNo: number; + constructor() { + super(); + this._header = new serverHeader(); + this._msgNo = 0; // 2 bytes + } - override size(): number { - return this._header.length + this.data.length; - } + override size(): number { + return this._header.length + this.data.length; + } - /** - * @deprecated - * @param {Buffer} buffer - * @returns {OldServerMessage} - */ - override _doDeserialize(buffer: Buffer): OldServerMessage { - this._header._doDeserialize(buffer); - this.setBuffer(buffer.subarray(this._header._size)); - if (this.data.length > 2) { - this._msgNo = this.data.readInt16LE(0); - } - return this; - } + /** + * @deprecated + * @param {Buffer} buffer + * @returns {OldServerMessage} + */ + override _doDeserialize(buffer: Buffer): OldServerMessage { + this._header._doDeserialize(buffer); + this.setBuffer(buffer.subarray(this._header._size)); + if (this.data.length > 2) { + this._msgNo = this.data.readInt16LE(0); + } + return this; + } - /** - * Serializes the current message into a buffer. - * - * This method allocates a new buffer with a size equal to the sum of the header length and 2 bytes. - * It then serializes the header and data into this buffer. - * - * @returns {Buffer} The serialized buffer containing the header and data. - */ - override serialize() { - const buffer = Buffer.alloc(this._header.length + 2); - this._header._doSerialize().copy(buffer); - this.data.copy(buffer, this._header._size); - return buffer; - } + /** + * Serializes the current message into a buffer. + * + * This method allocates a new buffer with a size equal to the sum of the header length and 2 bytes. + * It then serializes the header and data into this buffer. + * + * @returns {Buffer} The serialized buffer containing the header and data. + */ + override serialize() { + const buffer = Buffer.alloc(this._header.length + 2); + this._header._doSerialize().copy(buffer); + this.data.copy(buffer, this._header._size); + return buffer; + } - /** - * @deprecated - * @param {Buffer} buffer - */ - override setBuffer(buffer: Buffer) { - super.setBuffer(buffer); - this._header.length = buffer.length + this._header._size - 2; - } + /** + * @deprecated + * @param {Buffer} buffer + */ + override setBuffer(buffer: Buffer) { + super.setBuffer(buffer); + this._header.length = buffer.length + this._header._size - 2; + } - /** - * @deprecated - */ - updateMsgNo() { - this._msgNo = this.data.readInt16LE(0); - } + /** + * @deprecated + */ + updateMsgNo() { + this._msgNo = this.data.readInt16LE(0); + } - override toString() { - return `ServerMessage: ${JSON.stringify({ - header: this._header.toString(), - data: this.data.toString("hex"), - })}`; - } + override toString() { + return `ServerMessage: ${JSON.stringify({ + header: this._header.toString(), + data: this.data.toString('hex'), + })}`; + } - override toHexString() { - return this.serialize().toString("hex"); - } + override toHexString() { + return this.serialize().toString('hex'); + } - get sequenceNumber(): number { - return this._header.sequence; - } + get sequenceNumber(): number { + return this._header.sequence; + } } diff --git a/packages/shared/src/SerializedBufferOld.ts b/packages/shared/src/SerializedBufferOld.ts index f2d0322c3..d04c57232 100644 --- a/packages/shared/src/SerializedBufferOld.ts +++ b/packages/shared/src/SerializedBufferOld.ts @@ -1,5 +1,21 @@ -import type { SerializableInterface } from "rusty-motors-shared-packets"; -import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import type { SerializableInterface } from 'rusty-motors-shared-packets'; +import { SerializableMixin, AbstractSerializable } from './messageFactory.js'; /** * A raw message is a message that is not parsed into a specific type. @@ -8,43 +24,44 @@ import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; * @mixin {SerializableMixin} */ -export class SerializedBufferOld extends SerializableMixin( - AbstractSerializable, -) implements SerializableInterface { - constructor() { - super(); - } - - /** - * @param {Buffer} buffer - * @returns {SerializedBufferOld} - */ - override _doDeserialize(buffer: Buffer): SerializedBufferOld { - this.setBuffer(buffer); - return this; - } - - deserialize(data: Buffer): void { - this.setBuffer(data); - } - - serialize() { - return this.data; - } - - override toString() { - return `SerializedBuffer: ${this.serialize().toString("hex")}`; - } - - size() { - return this.data.length; - } - - getByteSize() { - return this.size(); - } - - toHexString() { - return this.data.toString("hex"); - } +export class SerializedBufferOld + extends SerializableMixin(AbstractSerializable) + implements SerializableInterface +{ + constructor() { + super(); + } + + /** + * @param {Buffer} buffer + * @returns {SerializedBufferOld} + */ + override _doDeserialize(buffer: Buffer): SerializedBufferOld { + this.setBuffer(buffer); + return this; + } + + deserialize(data: Buffer): void { + this.setBuffer(data); + } + + serialize() { + return this.data; + } + + override toString() { + return `SerializedBuffer: ${this.serialize().toString('hex')}`; + } + + size() { + return this.data.length; + } + + getByteSize() { + return this.size(); + } + + toHexString() { + return this.data.toString('hex'); + } } diff --git a/packages/shared/src/State.ts b/packages/shared/src/State.ts index 7d4798345..8481b0637 100644 --- a/packages/shared/src/State.ts +++ b/packages/shared/src/State.ts @@ -6,11 +6,10 @@ */ // eslint-disable-next-line no-unused-vars -import { Cipher, Decipher } from "crypto"; -import { SerializedBufferOld } from "./SerializedBufferOld.js"; -import { BufferSerializer } from "rusty-motors-shared-packets"; -import { ServerLogger } from "../index.js"; - +import { Cipher, Decipher } from 'crypto'; +import { SerializedBufferOld } from './SerializedBufferOld.js'; +import { BufferSerializer } from 'rusty-motors-shared-packets'; +import { ServerLogger } from 'rusty-motors-logger'; /** * State management for the gateway server. @@ -40,120 +39,120 @@ import { ServerLogger } from "../index.js"; * A pair of encryption ciphers. */ export class McosEncryptionPair { - _cipher: Cipher; - _decipher: Decipher; - /** - * Create a new encryption pair. - * - * This function creates a new encryption pair. It is used to encrypt and - * decrypt data sent to and from the client. - * - * @param {module:crypto.Cipher} cipher The cipher to use for encryption. - * @param {module:crypto.Decipher} decipher The decipher to use for decryption. - */ - constructor(cipher: Cipher, decipher: Decipher) { - this._cipher = cipher; - this._decipher = decipher; - } + _cipher: Cipher; + _decipher: Decipher; + /** + * Create a new encryption pair. + * + * This function creates a new encryption pair. It is used to encrypt and + * decrypt data sent to and from the client. + * + * @param {module:crypto.Cipher} cipher The cipher to use for encryption. + * @param {module:crypto.Decipher} decipher The decipher to use for decryption. + */ + constructor(cipher: Cipher, decipher: Decipher) { + this._cipher = cipher; + this._decipher = decipher; + } - /** - * @param {Buffer} data The data to encrypt. - * @returns {Buffer} The encrypted data. - */ - encrypt(data: Buffer): Buffer { - return this._cipher.update(data); - } + /** + * @param {Buffer} data The data to encrypt. + * @returns {Buffer} The encrypted data. + */ + encrypt(data: Buffer): Buffer { + return this._cipher.update(data); + } - /** - * @param {Buffer} data The data to decrypt. - * @returns {Buffer} The decrypted data. - */ - decrypt(data: Buffer): Buffer { - return this._decipher.update(data); - } + /** + * @param {Buffer} data The data to decrypt. + * @returns {Buffer} The decrypted data. + */ + decrypt(data: Buffer): Buffer { + return this._decipher.update(data); + } } /** * The encryption settings for a session. */ export class McosEncryption { - connectionId: string; - _commandEncryptionPair: McosEncryptionPair; - _dataEncryptionPair: McosEncryptionPair; - /** - * Create a new encryption object. - * - * @param {object} args - * @param {string} args.connectionId The connection id of the session that - * this encryption is for. - * @param {McosEncryptionPair} args.commandEncryptionPair The encryption - * pair for - * command packets. - * @param {McosEncryptionPair} args.dataEncryptionPair The encryption pair - * for data packets. - */ - constructor({ - connectionId, - commandEncryptionPair, - dataEncryptionPair, - }: { - connectionId: string; - commandEncryptionPair: McosEncryptionPair; - dataEncryptionPair: McosEncryptionPair; - }) { - this.connectionId = connectionId; - this._commandEncryptionPair = commandEncryptionPair; - this._dataEncryptionPair = dataEncryptionPair; - } + connectionId: string; + _commandEncryptionPair: McosEncryptionPair; + _dataEncryptionPair: McosEncryptionPair; + /** + * Create a new encryption object. + * + * @param {object} args + * @param {string} args.connectionId The connection id of the session that + * this encryption is for. + * @param {McosEncryptionPair} args.commandEncryptionPair The encryption + * pair for + * command packets. + * @param {McosEncryptionPair} args.dataEncryptionPair The encryption pair + * for data packets. + */ + constructor({ + connectionId, + commandEncryptionPair, + dataEncryptionPair, + }: { + connectionId: string; + commandEncryptionPair: McosEncryptionPair; + dataEncryptionPair: McosEncryptionPair; + }) { + this.connectionId = connectionId; + this._commandEncryptionPair = commandEncryptionPair; + this._dataEncryptionPair = dataEncryptionPair; + } - get commandEncryption() { - return this._commandEncryptionPair; - } + get commandEncryption() { + return this._commandEncryptionPair; + } - get dataEncryption() { - return this._dataEncryptionPair; - } + get dataEncryption() { + return this._dataEncryptionPair; + } } /** * A client session. */ export class McosSession { - connectionId: string; - gameId: number; - /** - * Create a new session. - * - * @param {object} args - * @param {string} args.connectionId A unique identifier for this session. - * @param {number} args.username The username of the user who owns this - * session. - */ - constructor({ - connectionId, - gameId, - }: { - connectionId: string; - gameId: number; - }) { - this.connectionId = connectionId; - this.gameId = gameId; - } + connectionId: string; + gameId: number; + /** + * Create a new session. + * + * @param {object} args + * @param {string} args.connectionId A unique identifier for this session. + * @param {number} args.username The username of the user who owns this + * session. + */ + constructor({ + connectionId, + gameId, + }: { + connectionId: string; + gameId: number; + }) { + this.connectionId = connectionId; + this.gameId = gameId; + } } type OnDataHandlerArgs = { - connectionId: string; - message: BufferSerializer; - log?: ServerLogger; + connectionId: string; + message: BufferSerializer; + log?: ServerLogger; }; export interface ServiceResponse { - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; } export type OnDataHandler = ( - args: OnDataHandlerArgs, + args: OnDataHandlerArgs, ) => Promise; /** * @param {OnDataHandlerArgs} args The arguments for the handler. @@ -177,12 +176,12 @@ export type OnDataHandler = ( * @interface */ export interface State { - filePaths: Record; - // sockets: Record; - encryptions: Record; - sessions: Record; - // queuedConnections: Record; - save: (state?: State) => void; + filePaths: Record; + // sockets: Record; + encryptions: Record; + sessions: Record; + // queuedConnections: Record; + save: (state?: State) => void; } /** @@ -202,27 +201,27 @@ export interface State { * @returns The initial state. */ export function createInitialState({ - saveFunction = saveStateToDatabase, + saveFunction = saveStateToDatabase, }: { - saveFunction?: (state: State) => void; + saveFunction?: (state: State) => void; }): State { - return { - filePaths: {}, - // sockets: {}, - encryptions: {}, - sessions: {}, - // queuedConnections: {}, - save: function (state?: State) { - if (typeof state === "undefined") { - state = this as State; - } - if (typeof saveFunction === "undefined") { - saveStateToDatabase(state); - return; - } - saveFunction(state); - }, - }; + return { + filePaths: {}, + // sockets: {}, + encryptions: {}, + sessions: {}, + // queuedConnections: {}, + save: function (state?: State) { + if (typeof state === 'undefined') { + state = this as State; + } + if (typeof saveFunction === 'undefined') { + saveStateToDatabase(state); + return; + } + saveFunction(state); + }, + }; } /** @@ -238,12 +237,12 @@ export function createInitialState({ * @returns {State} - The state with the encryption added. */ export function addEncryption(state: State, encryption: McosEncryption): State { - const encryptions = state.encryptions; - encryptions[encryption.connectionId] = encryption; - return { - ...state, - encryptions, - }; + const encryptions = state.encryptions; + encryptions[encryption.connectionId] = encryption; + return { + ...state, + encryptions, + }; } /** @@ -256,10 +255,10 @@ export function addEncryption(state: State, encryption: McosEncryption): State { * @returns {McosEncryption | undefined} The encryption with the given connection id, or undefined if no encryption */ export function getEncryption( - state: State, - connectionId: string, + state: State, + connectionId: string, ): McosEncryption | undefined { - return state.encryptions[connectionId]; + return state.encryptions[connectionId]; } /** @@ -275,15 +274,15 @@ export function getEncryption( * @returns {State} The state with the encryption updated. */ export function updateEncryption( - state: State, - encryption: McosEncryption, + state: State, + encryption: McosEncryption, ): State { - const encryptions = state.encryptions; - encryptions[encryption.connectionId] = encryption; - return { - ...state, - encryptions, - }; + const encryptions = state.encryptions; + encryptions[encryption.connectionId] = encryption; + return { + ...state, + encryptions, + }; } /** @@ -299,12 +298,12 @@ export function updateEncryption( * @returns {State} The state with the encryption removed. */ export function removeEncryption(state: State, connectionId: string): State { - const encryptions = state.encryptions; - delete encryptions[connectionId]; - return { - ...state, - encryptions, - }; + const encryptions = state.encryptions; + delete encryptions[connectionId]; + return { + ...state, + encryptions, + }; } /** @@ -320,12 +319,12 @@ export function removeEncryption(state: State, connectionId: string): State { * @returns {State} The state with the session added. */ export function addSession(state: State, session: McosSession): State { - const sessions = state.sessions; - sessions[session.connectionId] = session; - return { - ...state, - sessions, - }; + const sessions = state.sessions; + sessions[session.connectionId] = session; + return { + ...state, + sessions, + }; } /** @@ -342,19 +341,19 @@ export function addSession(state: State, session: McosSession): State { * @returns {State} The state with the session removed. */ export function removeSession(state: State, connectionId: string): State { - const sessions = state.sessions; - delete sessions[connectionId]; - return { - ...state, - sessions, - }; + const sessions = state.sessions; + delete sessions[connectionId]; + return { + ...state, + sessions, + }; } export function findSessionByConnectionId( - state: State, - connectionId: string, + state: State, + connectionId: string, ): McosSession | undefined { - return state.sessions[connectionId]; + return state.sessions[connectionId]; } /** @@ -365,7 +364,7 @@ export function findSessionByConnectionId( * @returns {State} The state from the database. */ export function fetchStateFromDatabase(): State { - return globalStateDatabase; + return globalStateDatabase; } /** @@ -376,7 +375,7 @@ export function fetchStateFromDatabase(): State { * @param {State} state The state to save to the database. */ function saveStateToDatabase(state: State) { - globalStateDatabase = state; + globalStateDatabase = state; } let globalStateDatabase = createInitialState({}); diff --git a/packages/shared/src/SubThread.ts b/packages/shared/src/SubThread.ts index f3e3f6b2f..cd9e4d735 100644 --- a/packages/shared/src/SubThread.ts +++ b/packages/shared/src/SubThread.ts @@ -2,47 +2,45 @@ * @module SubThread */ -import { EventEmitter } from "node:events"; -import { getServerLogger, ServerLogger } from "rusty-motors-shared"; - +import { EventEmitter } from 'node:events'; +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; export class SubThread extends EventEmitter { - name: string; - log: ServerLogger; - loopInterval: number; - timer: NodeJS.Timeout | null; - /** - * @param {string} name - * @param {erverLogger} log - * @param {number} [loopInterval=100] - */ - constructor( - name: string, - log: ServerLogger = getServerLogger( "SubThread"), - loopInterval: number = 100, - ) { - super(); - this.name = name; - this.log = log; - this.loopInterval = loopInterval; - this.timer = null; - this.init(); - } + name: string; + log: ServerLogger; + loopInterval: number; + timer: NodeJS.Timeout | null; + /** + * @param {string} name + * @param {erverLogger} log + * @param {number} [loopInterval=100] + */ + constructor( + name: string, + log: ServerLogger = getServerLogger('SubThread'), + loopInterval = 100, + ) { + super(); + this.name = name; + this.log = log; + this.loopInterval = loopInterval; + this.timer = null; + this.init(); + } - init() { - this.emit("initialized"); - // @ts-ignore - this.timer = setInterval(this.run.bind(this), this.loopInterval); - } + init() { + this.emit('initialized'); + this.timer = setInterval(this.run.bind(this), this.loopInterval); + } - run() { - // Intentionally left blank - } + run() { + // Intentionally left blank + } - shutdown() { - if (this.timer) { - clearInterval(this.timer); - } - this.emit("shutdownComplete"); - } + shutdown() { + if (this.timer) { + clearInterval(this.timer); + } + this.emit('shutdownComplete'); + } } diff --git a/packages/shared/src/interfaces.ts b/packages/shared/src/interfaces.ts deleted file mode 100644 index 710aa3c1f..000000000 --- a/packages/shared/src/interfaces.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @module interfaces - */ - -import type { SerializedBufferOld } from "./SerializedBufferOld.js"; -import { ServerLogger } from "../index.js"; - -export const name = "interfaces"; - -/** - * @exports - * @interface - */ - -export interface DatabaseManager { - updateSessionKey: ( - arg0: number, - arg1: string, - arg2: string, - arg3: string, - ) => Promise; - fetchSessionKeyByCustomerId: (arg0: number) => Promise; -} - -/** - * @exports - */ -export interface ConnectionRecord { - customerId: number; - connectionId: string; - sessionKey: string; - sKey: string; - contextId: string; -} - -interface SessionKeys { - sessionKey: string; - sKey: string; -} - -export interface GameMessageOpCode { - name: string; - value: number; - module: "Lobby" | "Login"; -} - -export interface UserRecordMini { - contextId: string; - customerId: number; - profileId: number; - } - -/** - * @exports - */ -export interface RaceLobbyRecord { - lobbyId: number; - raceTypeId: number; - turfId: number; - riffName: string; - eTurfName: string; -} - -export interface ServiceArgs { - connectionId: string; - message: SerializedBufferOld; - log?: ServerLogger; -} - -export interface KeypressEvent { - sequence: string; - name: string; - ctrl: boolean; - meta: boolean; - shift: boolean; -} diff --git a/packages/shared/src/legacyHeader.ts b/packages/shared/src/legacyHeader.ts index 82cd394b0..23b1a70d5 100644 --- a/packages/shared/src/legacyHeader.ts +++ b/packages/shared/src/legacyHeader.ts @@ -1,4 +1,4 @@ -import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; +import { SerializableMixin, AbstractSerializable } from './messageFactory.js'; /** * A legacy header is a 4 byte header with the following fields: @@ -9,50 +9,81 @@ import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; */ export class legacyHeader extends SerializableMixin(AbstractSerializable) { - _size: number; - id: number; - length: number; - constructor() { - super(); - this._size = 4; - this.id = 0; // 2 bytes - this.length = this._size; // 2 bytes - } - - /** - * @param {Buffer} buffer - */ - override _doDeserialize(buffer: Buffer) { - if (buffer.length < 4) { - throw Error(`Buffer length ${buffer.length} is too short to deserialize`); - } - - try { - this.id = buffer.readInt16BE(0); - this.length = buffer.readInt16BE(2); - } catch (error) { - const err = Error("Error deserializing buffer"); - err.cause = error; - throw err; - } - return this; - } - - override _doSerialize() { - const buffer = Buffer.alloc(this._size); - buffer.writeInt16BE(this.id, 0); - buffer.writeInt16BE(this.length, 2); - return buffer; - } - - override toString() { - return `LegacyHeader: ${JSON.stringify({ - id: this.id, - length: this.length, - })}`; - } - - static override get Size() { - return 4; - } + _size: number; + id: number; + length: number; + constructor() { + super(); + this._size = 4; + this.id = 0; // 2 bytes + this.length = this._size; // 2 bytes + } + + deserialize(buffer: Buffer) { + if (buffer.length < 4) { + throw Error( + `Buffer length ${buffer.length} is too short to deserialize`, + ); + } + + try { + this.id = buffer.readInt16BE(0); + this.length = buffer.readInt16BE(2); + } catch (error) { + const err = Error('Error deserializing buffer'); + err.cause = error; + throw err; + } + return this; + } + + serialize() { + const buffer = Buffer.alloc(this._size); + buffer.writeInt16BE(this.id, 0); + buffer.writeInt16BE(this.length, 2); + return buffer; + } + + /** + * @param {Buffer} buffer + */ + override _doDeserialize(buffer: Buffer) { + if (buffer.length < 4) { + throw Error( + `Buffer length ${buffer.length} is too short to deserialize`, + ); + } + + try { + this.id = buffer.readInt16BE(0); + this.length = buffer.readInt16BE(2); + } catch (error) { + const err = Error('Error deserializing buffer'); + err.cause = error; + throw err; + } + return this; + } + + override _doSerialize() { + const buffer = Buffer.alloc(this._size); + buffer.writeInt16BE(this.id, 0); + buffer.writeInt16BE(this.length, 2); + return buffer; + } + + override toString() { + return `LegacyHeader: ${JSON.stringify({ + id: this.id, + length: this.length, + })}`; + } + + static override get Size() { + return 4; + } + + get messageId() { + return this.id; + } } diff --git a/packages/shared/src/serializeString.ts b/packages/shared/src/serializeString.ts index 87a7377d6..469645886 100644 --- a/packages/shared/src/serializeString.ts +++ b/packages/shared/src/serializeString.ts @@ -1,3 +1,19 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + /** * Serializes a string with length prefix * @param {string} string @@ -7,15 +23,15 @@ */ export function serializeString( - string: string, - targetBuffer: Buffer, - offset: number, + string: string, + targetBuffer: Buffer, + offset: number, ): number { - const buffer = Buffer.alloc(4 + string.length + 1); - buffer.writeInt32BE(string.length + 1, 0); - const stringToWrite = string + "\0"; - buffer.write(stringToWrite, 4, stringToWrite.length, "utf8"); - buffer.copy(targetBuffer, offset); - offset += buffer.length; - return offset; + const buffer = Buffer.alloc(4 + string.length + 1); + buffer.writeInt32BE(string.length + 1, 0); + const stringToWrite = string + '\0'; + buffer.write(stringToWrite, 4, stringToWrite.length, 'utf8'); + buffer.copy(targetBuffer, offset); + offset += buffer.length; + return offset; } diff --git a/packages/shared/src/serializeStringRaw.ts b/packages/shared/src/serializeStringRaw.ts index edb70a513..ca1d9d43c 100644 --- a/packages/shared/src/serializeStringRaw.ts +++ b/packages/shared/src/serializeStringRaw.ts @@ -1,3 +1,19 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + /** * Serializes a raw string without length prefix * @param {string} string @@ -8,13 +24,13 @@ */ export function serializeStringRaw( - string: string, - targetBuffer: Buffer, - offset: number, - length?: number, + string: string, + targetBuffer: Buffer, + offset: number, + length?: number, ): number { - const stringToWrite = string; - targetBuffer.write(stringToWrite, offset, length ?? string.length, "utf8"); - offset += stringToWrite.length; - return offset; + const stringToWrite = string; + targetBuffer.write(stringToWrite, offset, length ?? string.length, 'utf8'); + offset += stringToWrite.length; + return offset; } diff --git a/packages/shared/src/serverHeader.ts b/packages/shared/src/serverHeader.ts index 59e1bf72b..e5439012a 100644 --- a/packages/shared/src/serverHeader.ts +++ b/packages/shared/src/serverHeader.ts @@ -1,4 +1,20 @@ -import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { SerializableMixin, AbstractSerializable } from './messageFactory.js'; /** * A server header is an 11 byte header with the following fields: @@ -9,60 +25,60 @@ import { SerializableMixin, AbstractSerializable } from "./messageFactory.js"; */ export class serverHeader extends SerializableMixin(AbstractSerializable) { - _size: number; - length: any; - mcoSig: string; - sequence: number; - flags: number; - constructor() { - super(); - this._size = 11; - this.length = this._size; // 2 bytes - this.mcoSig = "TOMC"; // 4 bytes - this.sequence = 0; // 4 bytes - this.flags = 0; // 1 byte - } + _size: number; + length: any; + mcoSig: string; + sequence: number; + flags: number; + constructor() { + super(); + this._size = 11; + this.length = this._size; // 2 bytes + this.mcoSig = 'TOMC'; // 4 bytes + this.sequence = 0; // 4 bytes + this.flags = 0; // 1 byte + } - size() { - return this._size; - } + size() { + return this._size; + } - /** - * @param {Buffer} buffer - * @returns {serverHeader} - * @throws {Error} If the buffer is too short - * @throws {Error} If the buffer is malformed - */ - override _doDeserialize(buffer: Buffer): serverHeader { - if (buffer.length < this._size) { - throw new Error( - `Buffer length ${buffer.length} is too short to deserialize`, - ); - } + /** + * @param {Buffer} buffer + * @returns {serverHeader} + * @throws {Error} If the buffer is too short + * @throws {Error} If the buffer is malformed + */ + override _doDeserialize(buffer: Buffer): serverHeader { + if (buffer.length < this._size) { + throw new Error( + `Buffer length ${buffer.length} is too short to deserialize`, + ); + } - try { - this.length = buffer.readInt16LE(0); - this.mcoSig = buffer.toString("utf8", 2, 6); - this.sequence = buffer.readInt32LE(6); - this.flags = buffer.readInt8(10); - } catch (error) { - const err = Error("Error deserializing buffer"); - err.cause = error; - throw err; - } - return this; - } + try { + this.length = buffer.readInt16LE(0); + this.mcoSig = buffer.toString('utf8', 2, 6); + this.sequence = buffer.readInt32LE(6); + this.flags = buffer.readInt8(10); + } catch (error) { + const err = Error('Error deserializing buffer'); + err.cause = error; + throw err; + } + return this; + } - override _doSerialize() { - const buffer = Buffer.alloc(this._size); - buffer.writeInt16LE(this.length, 0); - buffer.write(this.mcoSig, 2, 6, "utf8"); - buffer.writeInt32LE(this.sequence, 6); - buffer.writeInt8(this.flags, 10); - return buffer; - } + override _doSerialize() { + const buffer = Buffer.alloc(this._size); + buffer.writeInt16LE(this.length, 0); + buffer.write(this.mcoSig, 2, 6, 'utf8'); + buffer.writeInt32LE(this.sequence, 6); + buffer.writeInt8(this.flags, 10); + return buffer; + } - override toString() { - return `ServerHeader: length=${this.length}, mcoSig=${this.mcoSig}, sequence=${this.sequence}, flags=${this.flags}`; - } + override toString() { + return `ServerHeader: length=${this.length}, mcoSig=${this.mcoSig}, sequence=${this.sequence}, flags=${this.flags}`; + } } diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts new file mode 100644 index 000000000..82d4cb0dc --- /dev/null +++ b/packages/shared/src/types.ts @@ -0,0 +1,89 @@ +/** + * @module interfaces + */ + +import type { SerializedBufferOld } from './SerializedBufferOld.js'; +import type { ServerLogger } from 'rusty-motors-logger'; + +export type ConnectionRecord = { + customerId: number; + sessionKey: string; + sKey: string; + contextId: string; + connectionId: string; +}; + +/** + * @exports + * @interface + */ + +export interface DatabaseManager { + updateSessionKey: ( + arg0: number, + arg1: string, + arg2: string, + arg3: string, + ) => Promise; + fetchSessionKeyByCustomerId: (arg0: number) => Promise; +} + +/** + * @exports + */ +export interface GameMessageOpCode { + name: string; + value: number; + module: 'Lobby' | 'Login'; +} + +export interface UserRecordMini { + contextId: string; + customerId: number; + profileId: number; +} + +/** + * @exports + */ +export interface RaceLobbyRecord { + lobbyId: number; + raceTypeId: number; + turfId: number; + riffName: string; + eTurfName: string; +} + +export interface ServiceArgs { + connectionId: string; + message: SerializedBufferOld; + log?: ServerLogger; +} + +/** + * GNU Affero General Public License + * + * mcos is a game server, written from scratch, for an old game + * Copyright (C) <2017> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +export interface KeypressEvent { + sequence: string; + name: string; + ctrl: boolean; + meta: boolean; + shift: boolean; +} diff --git a/packages/shared/src/utility.ts b/packages/shared/src/utility.ts new file mode 100644 index 000000000..c82b12860 --- /dev/null +++ b/packages/shared/src/utility.ts @@ -0,0 +1,68 @@ +// mcos is a game server, written from scratch, for an old game +// Copyright (C) <2017> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Utility functions and color constants for shared package + +export function argbToInt( + alpha: number, + red: number, + green: number, + blue: number, +) { + return ( + ((alpha & 0xff) << 24) | + ((red & 0xff) << 16) | + ((green & 0xff) << 8) | + (blue & 0xff) + ); +} + +export function intToArgb(int: number) { + return { + alpha: (int >> 24) & 0xff, + red: (int >> 16) & 0xff, + green: (int >> 8) & 0xff, + blue: int & 0xff, + }; +} + +//skin colors +export const skin_pale = argbToInt(255, 255, 206, 165); //light pale +export const skin_tan = argbToInt(255, 206, 164, 122); //light tan +export const skin_brown = argbToInt(255, 112, 95, 78); //light brown +//shaded versions of the basic skin colors +export const dskin_pale = argbToInt(255, 140, 115, 90); //dark pale +export const dskin_tan = argbToInt(255, 124, 98, 72); //dark tan +export const dskin_brown = argbToInt(255, 63, 49, 35); //dark brown +//hair colors +export const hair_white = argbToInt(255, 255, 255, 255); //white +export const hair_platinum = argbToInt(255, 255, 242, 167); //platinum blonde +export const hair_blonde = argbToInt(255, 244, 219, 76); //blonde +export const hair_tan = argbToInt(255, 122, 100, 49); //tan +export const hair_red = argbToInt(255, 172, 69, 13); //red +export const hair_brown = argbToInt(255, 81, 65, 29); //brown +export const hair_black = argbToInt(255, 0, 0, 0); //black +//clothing colors +export const cloth_red = argbToInt(255, 212, 82, 82); //red +export const cloth_orange = argbToInt(255, 229, 139, 38); //orange +export const cloth_yellow = argbToInt(255, 255, 216, 0); //yellow +export const cloth_green = argbToInt(255, 112, 158, 113); //green +export const cloth_blue = argbToInt(255, 67, 81, 168); //blue +export const cloth_purple = argbToInt(255, 121, 80, 132); //purple +export const cloth_brown = argbToInt(255, 117, 104, 68); //brown +export const cloth_black = argbToInt(255, 68, 68, 68); //black +export const cloth_grey = argbToInt(255, 146, 143, 137); //grey +export const cloth_white = argbToInt(255, 255, 255, 255); //white diff --git a/packages/transactions/package.json b/packages/transactions/package.json index c89a77aa5..c7edf3b3e 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -14,7 +14,7 @@ "format": "npx @biomejs/biome format --write .", "test": "pnpm node --openssl-legacy-provider node_modules/vitest/vitest.mjs run --coverage", "test:crypto": "pnpm node --openssl-legacy-provider node_modules/vitest/vitest.mjs run --coverage", - "build": "tsc" + "build": "tsc" }, "keywords": [], "author": "", @@ -25,6 +25,7 @@ "vitest": "^3.1.3" }, "dependencies": { - "@sentry/profiling-node": "9.19.0" + "@sentry/profiling-node": "9.19.0", + "rusty-motors-logger": "workspace:^" } } \ No newline at end of file diff --git a/packages/transactions/src/_buyCarFromDealer.ts b/packages/transactions/src/_buyCarFromDealer.ts index 4c1b5352b..a739603f8 100644 --- a/packages/transactions/src/_buyCarFromDealer.ts +++ b/packages/transactions/src/_buyCarFromDealer.ts @@ -1,11 +1,11 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { ServerPacket } from "rusty-motors-shared-packets"; -import { GenericReplyMessage } from "./GenericReplyMessage.js"; +import { OldServerMessage } from 'rusty-motors-shared'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { ServerPacket } from 'rusty-motors-shared-packets'; +import { GenericReplyMessage } from './GenericReplyMessage.js'; -import { getServerLogger } from "rusty-motors-shared"; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_buyCarFromDealer"); +const defaultLogger = getServerLogger('handlers/_buyCarFromDealer'); class PurchaseStockCarMessage extends ServerPacket { dealerId = 0; @@ -19,13 +19,11 @@ class PurchaseStockCarMessage extends ServerPacket { } override getByteSize(): number { - return this.header.getByteSize() - + 2 - + 4 * 4; + return this.header.getByteSize() + 2 + 4 * 4; } override serialize(): Buffer { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } override deserialize(data: Buffer): ThisType { @@ -44,7 +42,6 @@ class PurchaseStockCarMessage extends ServerPacket { override toString() { return `PurchaseStockCarMessage: ${this.dealerId}, ${this.brandedPardId}, ${this.skinId}, ${this.tradeInCarId}`; } - } /** @@ -52,14 +49,16 @@ class PurchaseStockCarMessage extends ServerPacket { * @return {Promise} */ export async function _buyCarFromDealer({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const purchaseStockCarMessage = new PurchaseStockCarMessage(); - purchaseStockCarMessage.deserialize(packet.serialize()); + const purchaseStockCarMessage = new PurchaseStockCarMessage(); + purchaseStockCarMessage.deserialize(packet.serialize()); - log.debug(`[${connectionId}] Received PurchaseStockCarMessage: ${purchaseStockCarMessage.toString()}`); + log.debug( + `[${connectionId}] Received PurchaseStockCarMessage: ${purchaseStockCarMessage.toString()}`, + ); // TODO: Implement car purchase logic here @@ -68,18 +67,20 @@ export async function _buyCarFromDealer({ replyPacket.msgReply = 142; // PurchaseStockCarMessage replyPacket.result.writeUInt32LE(101, 0); // MC_SUCCESS replyPacket.data.writeUInt32LE(1000, 0); // New car ID - + log.debug( + `[${connectionId}] Sending GenericReplyMessage: ${replyPacket.toString()}`, + ); - log.debug(`[${connectionId}] Sending GenericReplyMessage: ${replyPacket.toString()}`); + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + responsePacket.setBuffer(replyPacket.serialize()); - responsePacket.setBuffer(replyPacket.serialize()); + log.debug( + `[${connectionId}] Sending response packet: ${responsePacket.toHexString()}`, + ); - log.debug(`[${connectionId}] Sending response packet: ${responsePacket.toHexString()}`); - - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getArcadeCarInfo.ts b/packages/transactions/src/_getArcadeCarInfo.ts index 7229c5114..88ca80451 100644 --- a/packages/transactions/src/_getArcadeCarInfo.ts +++ b/packages/transactions/src/_getArcadeCarInfo.ts @@ -1,38 +1,38 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { ArcadeCarInfo, ArcadeCarMessage } from "./ArcadeCarMessage.js"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { ArcadeCarInfo, ArcadeCarMessage } from './ArcadeCarMessage.js'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getArcadeCarInfo"); +const defaultLogger = getServerLogger('handlers/_getArcadeCarInfo'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function _getArcadeCarInfo({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const getArcadeCarInfoMessage = new GenericRequestMessage(); - getArcadeCarInfoMessage.deserialize(packet.data); + const getArcadeCarInfoMessage = new GenericRequestMessage(); + getArcadeCarInfoMessage.deserialize(packet.data); - log.debug(`Received Message: ${getArcadeCarInfoMessage.toString()}`); + log.debug(`Received Message: ${getArcadeCarInfoMessage.toString()}`); - const arcadeCarInfoMessage = new ArcadeCarMessage(); - arcadeCarInfoMessage._msgNo = 323; + const arcadeCarInfoMessage = new ArcadeCarMessage(); + arcadeCarInfoMessage._msgNo = 323; - const car1 = new ArcadeCarInfo(); - car1._brandedPartId = 113; // Bel-air - car1._lobbyId = 0; - arcadeCarInfoMessage.addCar(car1); + const car1 = new ArcadeCarInfo(); + car1._brandedPartId = 113; // Bel-air + car1._lobbyId = 0; + arcadeCarInfoMessage.addCar(car1); - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(arcadeCarInfoMessage.serialize()); + responsePacket.setBuffer(arcadeCarInfoMessage.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getGameUrls.ts b/packages/transactions/src/_getGameUrls.ts index dfca4abdb..51047080b 100644 --- a/packages/transactions/src/_getGameUrls.ts +++ b/packages/transactions/src/_getGameUrls.ts @@ -1,38 +1,38 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GameUrl, GameUrlsMessage } from "./GameUrlsMessage.js"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GameUrl, GameUrlsMessage } from './GameUrlsMessage.js'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getGameUrls"); +const defaultLogger = getServerLogger('handlers/_getGameUrls'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function _getGameUrls({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const getGameUrlsMessage = new GenericRequestMessage(); - getGameUrlsMessage.deserialize(packet.data); + const getGameUrlsMessage = new GenericRequestMessage(); + getGameUrlsMessage.deserialize(packet.data); - log.debug(`Received Message: ${getGameUrlsMessage.toString()}`); + log.debug(`Received Message: ${getGameUrlsMessage.toString()}`); - const gameUrlsMessage = new GameUrlsMessage(); - gameUrlsMessage._msgNo = 364; + const gameUrlsMessage = new GameUrlsMessage(); + gameUrlsMessage._msgNo = 364; - const url1 = new GameUrl(); - url1._urlId = 1; - url1.urlRef = "http://localhost:8080"; - gameUrlsMessage.addURL(url1); + const url1 = new GameUrl(); + url1._urlId = 1; + url1.urlRef = 'http://localhost:8080'; + gameUrlsMessage.addURL(url1); - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(gameUrlsMessage.serialize()); + responsePacket.setBuffer(gameUrlsMessage.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getOwnedParts.ts b/packages/transactions/src/_getOwnedParts.ts index 9abe1248e..68ab89ff8 100644 --- a/packages/transactions/src/_getOwnedParts.ts +++ b/packages/transactions/src/_getOwnedParts.ts @@ -1,41 +1,41 @@ import { - fetchStateFromDatabase, - findSessionByConnectionId, -} from "rusty-motors-shared"; -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { PartsAssemblyMessage } from "./PartsAssemblyMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; + fetchStateFromDatabase, + findSessionByConnectionId, +} from 'rusty-motors-shared'; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { PartsAssemblyMessage } from './PartsAssemblyMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getOwnedParts"); +const defaultLogger = getServerLogger('handlers/_getOwnedParts'); export async function _getOwnedParts({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const getOwnedPartsMessage = new GenericRequestMessage(); - getOwnedPartsMessage.deserialize(packet.data); + const getOwnedPartsMessage = new GenericRequestMessage(); + getOwnedPartsMessage.deserialize(packet.data); - log.debug(`Received Message: ${getOwnedPartsMessage.toString()}`); + log.debug(`Received Message: ${getOwnedPartsMessage.toString()}`); - const state = fetchStateFromDatabase(); + const state = fetchStateFromDatabase(); - const session = findSessionByConnectionId(state, connectionId); + const session = findSessionByConnectionId(state, connectionId); - if (!session) { - throw Error("Session not found"); - } + if (!session) { + throw Error('Session not found'); + } - const ownedPartsMessage = new PartsAssemblyMessage(session.gameId); - ownedPartsMessage._msgNo = 175; + const ownedPartsMessage = new PartsAssemblyMessage(session.gameId); + ownedPartsMessage._msgNo = 175; - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(ownedPartsMessage.serialize()); + responsePacket.setBuffer(ownedPartsMessage.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getOwnedVehicles.ts b/packages/transactions/src/_getOwnedVehicles.ts index 4a7267bd2..3dde5b9e9 100644 --- a/packages/transactions/src/_getOwnedVehicles.ts +++ b/packages/transactions/src/_getOwnedVehicles.ts @@ -1,51 +1,53 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { OwnedVehicle, OwnedVehiclesMessage } from "./OwnedVehiclesMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js" -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { OwnedVehicle, OwnedVehiclesMessage } from './OwnedVehiclesMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getOwnedVehicles"); +const defaultLogger = getServerLogger('handlers/_getOwnedVehicles'); const vehicleList = [ - { - personId: 1, - vehicleId: 1, - brandedPartId: 113, - }, + { + personId: 1, + vehicleId: 1, + brandedPartId: 113, + }, ]; export function getVehiclesForPerson(personId: number) { - return vehicleList.filter((vehicle) => vehicle.personId === personId); + return vehicleList.filter((vehicle) => vehicle.personId === personId); } -export async function _getOwnedVehicles( - { connectionId, packet, log = defaultLogger }: MessageHandlerArgs -): Promise { - const getOwnedVehiclesMessage = new GenericRequestMessage(); - getOwnedVehiclesMessage.deserialize(packet.data); +export async function _getOwnedVehicles({ + connectionId, + packet, + log = defaultLogger, +}: MessageHandlerArgs): Promise { + const getOwnedVehiclesMessage = new GenericRequestMessage(); + getOwnedVehiclesMessage.deserialize(packet.data); - log.debug(`Received Message: ${getOwnedVehiclesMessage.toString()}`); + log.debug(`Received Message: ${getOwnedVehiclesMessage.toString()}`); - const personId = getOwnedVehiclesMessage.data.readUInt32LE(0); + const personId = getOwnedVehiclesMessage.data.readUInt32LE(0); - const ownedVehiclesMessage = new OwnedVehiclesMessage(); + const ownedVehiclesMessage = new OwnedVehiclesMessage(); - const vehicles = getVehiclesForPerson(personId); + const vehicles = getVehiclesForPerson(personId); - for (const vehicle of vehicles) { - const ownedVehicle = new OwnedVehicle(); - ownedVehicle._vehicleId = vehicle.vehicleId; - ownedVehicle._brandedPartId = vehicle.brandedPartId; - ownedVehiclesMessage.addVehicle(ownedVehicle); - } + for (const vehicle of vehicles) { + const ownedVehicle = new OwnedVehicle(); + ownedVehicle._vehicleId = vehicle.vehicleId; + ownedVehicle._brandedPartId = vehicle.brandedPartId; + ownedVehiclesMessage.addVehicle(ownedVehicle); + } - ownedVehiclesMessage._msgNo = 173; + ownedVehiclesMessage._msgNo = 173; - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(ownedVehiclesMessage.serialize()); + responsePacket.setBuffer(ownedVehiclesMessage.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getPlayerInfo.ts b/packages/transactions/src/_getPlayerInfo.ts index a7d6862eb..48bfce547 100644 --- a/packages/transactions/src/_getPlayerInfo.ts +++ b/packages/transactions/src/_getPlayerInfo.ts @@ -1,56 +1,54 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { PlayerInfoMessage } from "./PlayerInfoMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { PlayerInfoMessage } from './PlayerInfoMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getPlayerInfo"); +const defaultLogger = getServerLogger('handlers/_getPlayerInfo'); export async function _getPlayerInfo({ - connectionId, - packet, - log = defaultLogger, -}: MessageHandlerArgs - -): Promise { - const getPlayerInfoMessage = new GenericRequestMessage(); - getPlayerInfoMessage.deserialize(packet.data); - - log.debug( - `[${connectionId}] Received GenericRequestMessage: ${getPlayerInfoMessage.toString()}`, - ); - - const playerId = getPlayerInfoMessage.data.readUInt32LE(0); - - log.debug(`[${connectionId}] Player ID: ${playerId}`); - - try { - const playerInfoMessage = new PlayerInfoMessage(); - playerInfoMessage._msgNo = 108; - playerInfoMessage._playerId = playerId; - playerInfoMessage._playerName = "Dr Brown"; - playerInfoMessage._currentLevel = 1; - playerInfoMessage._currentClub = 0; - playerInfoMessage._maxInventorySlots = 100; - playerInfoMessage._numberOfInventorySlotsUsed = 0; - playerInfoMessage._bankBalance = 50; - playerInfoMessage._numberOfPointsToNextLevel = 3; - - log.debug( - `[${connectionId}] Sending PlayerInfoMessage: ${playerInfoMessage.toString()}`, - ); - - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; - - responsePacket.setBuffer(playerInfoMessage.serialize()); - - return { connectionId: connectionId, messages: [responsePacket] }; - } catch (error) { - const err = Error(`[${connectionId}] Error handling getPlayerInfo`, { - cause: error, - }); - throw err; - } + connectionId, + packet, + log = defaultLogger, +}: MessageHandlerArgs): Promise { + const getPlayerInfoMessage = new GenericRequestMessage(); + getPlayerInfoMessage.deserialize(packet.data); + + log.debug( + `[${connectionId}] Received GenericRequestMessage: ${getPlayerInfoMessage.toString()}`, + ); + + const playerId = getPlayerInfoMessage.data.readUInt32LE(0); + + log.debug(`[${connectionId}] Player ID: ${playerId}`); + + try { + const playerInfoMessage = new PlayerInfoMessage(); + playerInfoMessage._msgNo = 108; + playerInfoMessage._playerId = playerId; + playerInfoMessage._playerName = 'Dr Brown'; + playerInfoMessage._currentLevel = 1; + playerInfoMessage._currentClub = 0; + playerInfoMessage._maxInventorySlots = 100; + playerInfoMessage._numberOfInventorySlotsUsed = 0; + playerInfoMessage._bankBalance = 50; + playerInfoMessage._numberOfPointsToNextLevel = 3; + + log.debug( + `[${connectionId}] Sending PlayerInfoMessage: ${playerInfoMessage.toString()}`, + ); + + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; + + responsePacket.setBuffer(playerInfoMessage.serialize()); + + return { connectionId: connectionId, messages: [responsePacket] }; + } catch (error) { + const err = Error(`[${connectionId}] Error handling getPlayerInfo`, { + cause: error, + }); + throw err; + } } diff --git a/packages/transactions/src/_getPlayerPhysical.ts b/packages/transactions/src/_getPlayerPhysical.ts index d81011b64..04e3726c7 100644 --- a/packages/transactions/src/_getPlayerPhysical.ts +++ b/packages/transactions/src/_getPlayerPhysical.ts @@ -1,52 +1,53 @@ import { - cloth_white, - cloth_yellow, - hair_red, - OldServerMessage, - skin_pale, -} from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { PlayerPhysicalMessage } from "./PlayerPhysicalMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("handlers/_getPlayerPhysical"); - + cloth_white, + cloth_yellow, + hair_red, + OldServerMessage, + skin_pale, +} from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { PlayerPhysicalMessage } from './PlayerPhysicalMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; + +const defaultLogger = getServerLogger('handlers/_getPlayerPhysical'); export async function _getPlayerPhysical({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const getPlayerPhysicalMessage = new GenericRequestMessage(); - getPlayerPhysicalMessage.deserialize(packet.data); + const getPlayerPhysicalMessage = new GenericRequestMessage(); + getPlayerPhysicalMessage.deserialize(packet.data); - log.debug( - `[${connectionId}] Received GenericRequestMessage: ${getPlayerPhysicalMessage.toString()}`, - ); + log.debug( + `[${connectionId}] Received GenericRequestMessage: ${getPlayerPhysicalMessage.toString()}`, + ); - const playerId = getPlayerPhysicalMessage.data.readUInt32LE(0); + const playerId = getPlayerPhysicalMessage.data.readUInt32LE(0); - const playerPhysicalMessage = new PlayerPhysicalMessage(); - playerPhysicalMessage._msgNo = 265; - playerPhysicalMessage._playerId = playerId; - playerPhysicalMessage._bodytype = 5; - playerPhysicalMessage._hairColor = hair_red; - playerPhysicalMessage._skinColor = skin_pale; - playerPhysicalMessage._shirtColor = cloth_white; - playerPhysicalMessage._pantsColor = cloth_yellow; + const playerPhysicalMessage = new PlayerPhysicalMessage(); + playerPhysicalMessage._msgNo = 265; + playerPhysicalMessage._playerId = playerId; + playerPhysicalMessage._bodytype = 5; + playerPhysicalMessage._hairColor = hair_red; + playerPhysicalMessage._skinColor = skin_pale; + playerPhysicalMessage._shirtColor = cloth_white; + playerPhysicalMessage._pantsColor = cloth_yellow; - log.debug( - `[${connectionId}] Sending PlayerPhysicalMessage: ${playerPhysicalMessage.toString()}`, - ); + log.debug( + `[${connectionId}] Sending PlayerPhysicalMessage: ${playerPhysicalMessage.toString()}`, + ); - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(playerPhysicalMessage.serialize()); + responsePacket.setBuffer(playerPhysicalMessage.serialize()); - log.debug(`[${connectionId}] Sending response: ${responsePacket.toString()}`); + log.debug( + `[${connectionId}] Sending response: ${responsePacket.toString()}`, + ); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getPlayerRaceHistory.ts b/packages/transactions/src/_getPlayerRaceHistory.ts index 730ce621b..fd16bdd87 100644 --- a/packages/transactions/src/_getPlayerRaceHistory.ts +++ b/packages/transactions/src/_getPlayerRaceHistory.ts @@ -1,66 +1,63 @@ -import { ServerMessage } from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { - PlayerRacingHistoryMessage, -} from "./PlayerRacingHistoryMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getRacingHistoryRecords } from "./database/racingHistoryRecords.js"; -import { GenericReplyPayload } from "rusty-motors-shared-packets"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("handlers/_getPlayerRaceHistory"); +import { ServerMessage } from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { PlayerRacingHistoryMessage } from './PlayerRacingHistoryMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getRacingHistoryRecords } from './database/racingHistoryRecords.js'; +import { GenericReplyPayload } from 'rusty-motors-shared-packets'; +import { getServerLogger } from 'rusty-motors-logger'; +const defaultLogger = getServerLogger('handlers/_getPlayerRaceHistory'); export async function _getPlayerRaceHistory({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - log.debug(`[${connectionId}] Handling _getPlayerRaceHistory...`); + log.debug(`[${connectionId}] Handling _getPlayerRaceHistory...`); - const getPlayerRaceHistoryMessage = new GenericRequestMessage(); - getPlayerRaceHistoryMessage.deserialize(packet.data); + const getPlayerRaceHistoryMessage = new GenericRequestMessage(); + getPlayerRaceHistoryMessage.deserialize(packet.data); - log.debug(`Received Message: ${getPlayerRaceHistoryMessage.toString()}`); + log.debug(`Received Message: ${getPlayerRaceHistoryMessage.toString()}`); - const playerId = getPlayerRaceHistoryMessage.data.readInt32LE(0); + const playerId = getPlayerRaceHistoryMessage.data.readInt32LE(0); - log.debug(`Player ID: ${playerId}`); + log.debug(`Player ID: ${playerId}`); - const racingHistoryRecords = getRacingHistoryRecords(playerId); + const racingHistoryRecords = getRacingHistoryRecords(playerId); - const reply = new GenericReplyPayload(); - reply.setMessageId(0x101); // MC_SUCCESS - reply.msgReply = 362; // 0x16A + const reply = new GenericReplyPayload(); + reply.setMessageId(0x101); // MC_SUCCESS + reply.msgReply = 362; // 0x16A - const playerRacingHistoryMessage = new PlayerRacingHistoryMessage(); - playerRacingHistoryMessage._msgId = 362; - playerRacingHistoryMessage._userId = playerId; - playerRacingHistoryMessage._numRaces = racingHistoryRecords.length; + const playerRacingHistoryMessage = new PlayerRacingHistoryMessage(); + playerRacingHistoryMessage._msgId = 362; + playerRacingHistoryMessage._userId = playerId; + playerRacingHistoryMessage._numRaces = racingHistoryRecords.length; - // for (const record of racingHistoryRecords) { - // const recordEntry = new RacingHistoryRecordEntry(); - // recordEntry.raceType = record.raceType; - // recordEntry.numberOfRacesEntered = record.numberOfRacesEntered; - // recordEntry.numberOfRacesFinished = record.numberOfRacesFinished; - // recordEntry.numberOfRacesWon = record.numberOfRacesWon; - // recordEntry.numberOfCarsWon = record.numberOfCarsWon; - // recordEntry.numberOfCarsLost = record.numberOfCarsLost; - // recordEntry.numberOfChampionshipsWon = record.numberOfChampionshipsWon; - // recordEntry.cashWon = record.cashWon; + // for (const record of racingHistoryRecords) { + // const recordEntry = new RacingHistoryRecordEntry(); + // recordEntry.raceType = record.raceType; + // recordEntry.numberOfRacesEntered = record.numberOfRacesEntered; + // recordEntry.numberOfRacesFinished = record.numberOfRacesFinished; + // recordEntry.numberOfRacesWon = record.numberOfRacesWon; + // recordEntry.numberOfCarsWon = record.numberOfCarsWon; + // recordEntry.numberOfCarsLost = record.numberOfCarsLost; + // recordEntry.numberOfChampionshipsWon = record.numberOfChampionshipsWon; + // recordEntry.cashWon = record.cashWon; - // playerRacingHistoryMessage.addRecord(recordEntry); - // } + // playerRacingHistoryMessage.addRecord(recordEntry); + // } - playerRacingHistoryMessage._expectMore = false; + playerRacingHistoryMessage._expectMore = false; - const responsePacket = new ServerMessage( - packet.sequenceNumber, - 8, - playerRacingHistoryMessage.serialize(), - ); + const responsePacket = new ServerMessage( + packet.sequenceNumber, + 8, + playerRacingHistoryMessage.serialize(), + ); - log.debug(`Sending Message: ${playerRacingHistoryMessage.toString()}`); + log.debug(`Sending Message: ${playerRacingHistoryMessage.toString()}`); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getStockCarInfo.ts b/packages/transactions/src/_getStockCarInfo.ts index b2862fe6e..ec6387623 100644 --- a/packages/transactions/src/_getStockCarInfo.ts +++ b/packages/transactions/src/_getStockCarInfo.ts @@ -1,42 +1,42 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { StockCar } from "./StockCar.js"; -import { StockCarInfoMessage } from "./StockCarInfoMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { StockCar } from './StockCar.js'; +import { StockCarInfoMessage } from './StockCarInfoMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getStockCarInfo"); +const defaultLogger = getServerLogger('handlers/_getStockCarInfo'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function _getStockCarInfo({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const getStockCarInfoMessage = new GenericRequestMessage(); - getStockCarInfoMessage.deserialize(packet.data); + const getStockCarInfoMessage = new GenericRequestMessage(); + getStockCarInfoMessage.deserialize(packet.data); - log.debug(`Received Message: ${getStockCarInfoMessage.toString()}`); + log.debug(`Received Message: ${getStockCarInfoMessage.toString()}`); - const stockCarInfoMessage = new StockCarInfoMessage(200, 0, 105); - stockCarInfoMessage.starterCash = 200; - stockCarInfoMessage.dealerId = 8; - stockCarInfoMessage.brand = 105; + const stockCarInfoMessage = new StockCarInfoMessage(200, 0, 105); + stockCarInfoMessage.starterCash = 200; + stockCarInfoMessage.dealerId = 8; + stockCarInfoMessage.brand = 105; - stockCarInfoMessage.addStockCar(new StockCar(113, 20, false)); // Bel-air - stockCarInfoMessage.addStockCar(new StockCar(104, 15, true)); // Fairlane - Deal of the day - stockCarInfoMessage.addStockCar(new StockCar(402, 20, false)); // Century + stockCarInfoMessage.addStockCar(new StockCar(113, 20, false)); // Bel-air + stockCarInfoMessage.addStockCar(new StockCar(104, 15, true)); // Fairlane - Deal of the day + stockCarInfoMessage.addStockCar(new StockCar(402, 20, false)); // Century - log.debug(`Sending Message: ${stockCarInfoMessage.toString()}`); + log.debug(`Sending Message: ${stockCarInfoMessage.toString()}`); - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(stockCarInfoMessage.serialize()); + responsePacket.setBuffer(stockCarInfoMessage.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_getTunables.ts b/packages/transactions/src/_getTunables.ts index c4de2192f..4f80e81d9 100644 --- a/packages/transactions/src/_getTunables.ts +++ b/packages/transactions/src/_getTunables.ts @@ -1,33 +1,33 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericRequestMessage } from "./GenericRequestMessage.js"; -import { TunablesMessage } from "./TunablesMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericRequestMessage } from './GenericRequestMessage.js'; +import { TunablesMessage } from './TunablesMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_getTunables"); +const defaultLogger = getServerLogger('handlers/_getTunables'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function _getTunables({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const getTunablesMessage = new GenericRequestMessage(); - getTunablesMessage.deserialize(packet.data); + const getTunablesMessage = new GenericRequestMessage(); + getTunablesMessage.deserialize(packet.data); - log.debug(`Received Message: ${getTunablesMessage.toString()}`); + log.debug(`Received Message: ${getTunablesMessage.toString()}`); - const tunablesMessage = new TunablesMessage(); - tunablesMessage._msgNo = 390; + const tunablesMessage = new TunablesMessage(); + tunablesMessage._msgNo = 390; - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = packet.sequenceNumber; - responsePacket._header.flags = 8; + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = packet.sequenceNumber; + responsePacket._header.flags = 8; - responsePacket.setBuffer(tunablesMessage.serialize()); + responsePacket.setBuffer(tunablesMessage.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/_logout.ts b/packages/transactions/src/_logout.ts index 3ce484f80..ecd16170c 100644 --- a/packages/transactions/src/_logout.ts +++ b/packages/transactions/src/_logout.ts @@ -1,30 +1,30 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericReplyMessage } from "./GenericReplyMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericReplyMessage } from './GenericReplyMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/_logout"); +const defaultLogger = getServerLogger('handlers/_logout'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function _logout({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - log.debug(`[${connectionId}] Logout request: ${packet.toHexString()}`); - // Create new response packet - const pReply = new GenericReplyMessage(); - pReply.msgNo = 101; - pReply.msgReply = 106; - const rPacket = new OldServerMessage(); - rPacket._header.sequence = packet.sequenceNumber + 1; - rPacket._header.flags = 8; - rPacket.setBuffer(pReply.serialize()); + log.debug(`[${connectionId}] Logout request: ${packet.toHexString()}`); + // Create new response packet + const pReply = new GenericReplyMessage(); + pReply.msgNo = 101; + pReply.msgReply = 106; + const rPacket = new OldServerMessage(); + rPacket._header.sequence = packet.sequenceNumber + 1; + rPacket._header.flags = 8; + rPacket.setBuffer(pReply.serialize()); - log.debug(`[${connectionId}] Logout response: ${rPacket.toHexString()}`); + log.debug(`[${connectionId}] Logout response: ${rPacket.toHexString()}`); - return { connectionId, messages: [rPacket] }; + return { connectionId, messages: [rPacket] }; } diff --git a/packages/transactions/src/clientConnect.ts b/packages/transactions/src/clientConnect.ts index 3dc7f96b7..71031df8a 100644 --- a/packages/transactions/src/clientConnect.ts +++ b/packages/transactions/src/clientConnect.ts @@ -1,107 +1,108 @@ import { - createCommandEncryptionPair, - createDataEncryptionPair, -} from "rusty-motors-gateway"; + createCommandEncryptionPair, + createDataEncryptionPair, +} from 'rusty-motors-gateway'; import { - McosEncryption, - McosSession, - addEncryption, - addSession, - fetchStateFromDatabase, - getEncryption, -} from "rusty-motors-shared"; -import { OldServerMessage, getServerLogger } from "rusty-motors-shared"; -import { GenericReplyMessage } from "./GenericReplyMessage.js"; -import { TClientConnectMessage } from "./TClientConnectMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { databaseManager } from "rusty-motors-database"; -const defaultLogger = getServerLogger("clientConnect"); + McosEncryption, + McosSession, + addEncryption, + addSession, + fetchStateFromDatabase, + getEncryption, +} from 'rusty-motors-shared'; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericReplyMessage } from './GenericReplyMessage.js'; +import { TClientConnectMessage } from './TClientConnectMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { databaseService } from 'rusty-motors-database'; +import { getServerLogger } from 'rusty-motors-logger'; +const defaultLogger = getServerLogger('clientConnect'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function clientConnect({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - /** - * Let's turn it into a ClientConnectMsg - */ - const newMessage = new TClientConnectMessage(); + /** + * Let's turn it into a ClientConnectMsg + */ + const newMessage = new TClientConnectMessage(); - newMessage.deserialize(packet.serialize()); + newMessage.deserialize(packet.serialize()); - log.debug(`ClientConnectMsg: ${newMessage.toString()}`); + log.debug(`ClientConnectMsg: ${newMessage.toString()}`); - const customerId = newMessage._customerId; - if (typeof customerId !== "number") { - throw new TypeError( - `customerId is wrong type. Expected 'number', got ${typeof customerId}`, - ); - } + const customerId = newMessage._customerId; + if (typeof customerId !== 'number') { + throw new TypeError( + `customerId is wrong type. Expected 'number', got ${typeof customerId}`, + ); + } - const state = fetchStateFromDatabase(); + const state = fetchStateFromDatabase(); - const existingEncryption = getEncryption(state, connectionId); + const existingEncryption = getEncryption(state, connectionId); - if (existingEncryption) { - log.debug("Encryption already exists for this connection"); - return { connectionId, messages: [] }; - } + if (existingEncryption) { + log.debug('Encryption already exists for this connection'); + return { connectionId, messages: [] }; + } - let result; + let result; - log.debug(`Looking up the session key for ${customerId}...`); + log.debug(`Looking up the session key for ${customerId}...`); - result = await databaseManager.fetchSessionKeyByCustomerId(customerId); + result = await databaseService.fetchSessionKeyByCustomerId(customerId); - if (!result) { - log.error(`Session key not found for ${customerId}`); - throw new Error(`Session key not found + if (!result) { + log.error(`Session key not found for ${customerId}`); + throw new Error(`Session key not found for customer ${customerId}`); - } + } - log.debug(`Session key found for ${customerId}`); + log.debug(`Session key found for ${customerId}`); - const newCommandEncryptionPair = createCommandEncryptionPair( - result.sessionKey, - ); + const newCommandEncryptionPair = createCommandEncryptionPair( + result.sessionKey, + ); - const newDataEncryptionPair = createDataEncryptionPair(result.sessionKey); + const newDataEncryptionPair = createDataEncryptionPair(result.sessionKey); - const newEncryption = new McosEncryption({ - connectionId, - commandEncryptionPair: newCommandEncryptionPair, - dataEncryptionPair: newDataEncryptionPair, - }); + const newEncryption = new McosEncryption({ + connectionId, + commandEncryptionPair: newCommandEncryptionPair, + dataEncryptionPair: newDataEncryptionPair, + }); - const updatedState = addEncryption(state, newEncryption); + const updatedState = addEncryption(state, newEncryption); - const session = new McosSession({ - connectionId, - gameId: newMessage._personaId, - }); + const session = new McosSession({ + connectionId, + gameId: newMessage._personaId, + }); - addSession(updatedState, session).save(); + addSession(updatedState, session).save(); - const personaId = newMessage._personaId; + const personaId = newMessage._personaId; - const personaName = newMessage._personaName; + const personaName = newMessage._personaName; - log.debug(`cust: ${customerId} ID: ${personaId} Name: ${personaName}`); + log.debug(`cust: ${customerId} ID: ${personaId} Name: ${personaName}`); - // Create new response packet - const pReply = new GenericReplyMessage(); - pReply.msgNo = 101; - pReply.msgReply = newMessage._msgNo; + // Create new response packet + const pReply = new GenericReplyMessage(); + pReply.msgNo = 101; + pReply.msgReply = newMessage._msgNo; - const responsePacket = new OldServerMessage(); - responsePacket.setBuffer(pReply.serialize()); - responsePacket._header.sequence = packet.sequenceNumber; + const responsePacket = new OldServerMessage(); + responsePacket.setBuffer(pReply.serialize()); + responsePacket._header.sequence = packet.sequenceNumber; - log.debug(`Response: ${responsePacket.serialize().toString("hex")}`); + log.debug(`Response: ${responsePacket.serialize().toString('hex')}`); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/getLobbies.ts b/packages/transactions/src/getLobbies.ts index 67f9d6724..9f793aeba 100644 --- a/packages/transactions/src/getLobbies.ts +++ b/packages/transactions/src/getLobbies.ts @@ -1,10 +1,10 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { EntryFeePurseMessage, PurseEntry } from "./EntryFeePurseMessage.js"; -import { LobbyInfo, LobbyMessage } from "./LobbyMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { EntryFeePurseMessage, PurseEntry } from './EntryFeePurseMessage.js'; +import { LobbyInfo, LobbyMessage } from './LobbyMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/getLobbies"); +const defaultLogger = getServerLogger('handlers/getLobbies'); /** * @param {MessageHandlerArgs} args @@ -12,77 +12,82 @@ const defaultLogger = getServerLogger("handlers/getLobbies"); */ async function _getLobbies({ - connectionId, - packet, + connectionId, + packet, }: MessageHandlerArgs): Promise { - defaultLogger.debug(`[${connectionId}] Received getLobbies packet ${packet.toString()}`); + defaultLogger.debug( + `[${connectionId}] Received getLobbies packet ${packet.toString()}`, + ); - defaultLogger.debug(`[${connectionId}] Sending lobbies response...`); + defaultLogger.debug(`[${connectionId}] Sending lobbies response...`); - // Create new response packet - const lobbiesResponsePacket = new OldServerMessage(); - lobbiesResponsePacket._header.sequence = packet.sequenceNumber; - lobbiesResponsePacket._header.flags = 8; + // Create new response packet + const lobbiesResponsePacket = new OldServerMessage(); + lobbiesResponsePacket._header.sequence = packet.sequenceNumber; + lobbiesResponsePacket._header.flags = 8; - const lobbyResponse = new LobbyMessage(); - lobbyResponse._msgNo = 325; - lobbyResponse._shouldExpectMoreMessages = false; + const lobbyResponse = new LobbyMessage(); + lobbyResponse._msgNo = 325; + lobbyResponse._shouldExpectMoreMessages = false; - const lobby = new LobbyInfo(); - lobby._lobbyId = 2; - lobby._lobbyName = "LOBBY"; - lobby._topDog = "Drazi Crendraven"; - lobby._maxNumberPlayers = 8; + const lobby = new LobbyInfo(); + lobby._lobbyId = 2; + lobby._lobbyName = 'LOBBY'; + lobby._topDog = 'Drazi Crendraven'; + lobby._maxNumberPlayers = 8; - defaultLogger.debug(`[${connectionId}] Sending lobby: ${lobby.toString()}`); + defaultLogger.debug(`[${connectionId}] Sending lobby: ${lobby.toString()}`); - lobbyResponse.addLobby(lobby); + lobbyResponse.addLobby(lobby); - defaultLogger.debug(`[${connectionId}] Sending lobbyResponse: ${lobbyResponse.toString()}` - ); + defaultLogger.debug( + `[${connectionId}] Sending lobbyResponse: ${lobbyResponse.toString()}`, + ); - lobbiesResponsePacket.setBuffer(lobbyResponse.serialize()); + lobbiesResponsePacket.setBuffer(lobbyResponse.serialize()); - // Handle purse entries - const purseEntry = new PurseEntry(); - purseEntry._entryFee = 100; - purseEntry._purse = 1000; + // Handle purse entries + const purseEntry = new PurseEntry(); + purseEntry._entryFee = 100; + purseEntry._purse = 1000; - const perseEntryResponse = new EntryFeePurseMessage(); - perseEntryResponse._msgNo = 408; - perseEntryResponse._shouldExpectMoreMessages = false; - perseEntryResponse.addEntry(purseEntry); + const perseEntryResponse = new EntryFeePurseMessage(); + perseEntryResponse._msgNo = 408; + perseEntryResponse._shouldExpectMoreMessages = false; + perseEntryResponse.addEntry(purseEntry); - defaultLogger.debug(`[${connectionId}] Sending purseEntryResponse: ${perseEntryResponse.toString()}`); + defaultLogger.debug( + `[${connectionId}] Sending purseEntryResponse: ${perseEntryResponse.toString()}`, + ); - const perseEntriesResponsePacket = new OldServerMessage(); - perseEntriesResponsePacket._header.sequence = packet.sequenceNumber; - perseEntriesResponsePacket._header.flags = 8; + const perseEntriesResponsePacket = new OldServerMessage(); + perseEntriesResponsePacket._header.sequence = packet.sequenceNumber; + perseEntriesResponsePacket._header.flags = 8; - perseEntriesResponsePacket.setBuffer(perseEntryResponse.serialize()); + perseEntriesResponsePacket.setBuffer(perseEntryResponse.serialize()); - return { - connectionId, - messages: [lobbiesResponsePacket, perseEntriesResponsePacket], - }; + return { + connectionId, + messages: [lobbiesResponsePacket, perseEntriesResponsePacket], + }; } /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function getLobbies({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - const result = await _getLobbies({ connectionId, packet, log }); - log.debug("Dumping Lobbies response packet..."); - result.messages.forEach((msg) => { - log.debug(msg.toString()); - }); - log.debug(result.messages.join().toString()); - return { - connectionId, - messages: result.messages, - }; + const result = await _getLobbies({ connectionId, packet, log }); + log.debug('Dumping Lobbies response packet...'); + result.messages.forEach((msg) => { + log.debug(msg.toString()); + }); + log.debug(result.messages.join().toString()); + return { + connectionId, + messages: result.messages, + }; } diff --git a/packages/transactions/src/handlers.ts b/packages/transactions/src/handlers.ts index 215ea273c..fc7c98eb2 100644 --- a/packages/transactions/src/handlers.ts +++ b/packages/transactions/src/handlers.ts @@ -14,103 +14,103 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { ServerLogger } from "rusty-motors-shared"; -import { _getArcadeCarInfo } from "./_getArcadeCarInfo.js"; -import { _getGameUrls } from "./_getGameUrls.js"; -import { _getOwnedParts } from "./_getOwnedParts.js"; -import { _getOwnedVehicles } from "./_getOwnedVehicles.js"; -import { _getPlayerInfo } from "./_getPlayerInfo.js"; -import { _getPlayerPhysical } from "./_getPlayerPhysical.js"; -import { _getPlayerRaceHistory } from "./_getPlayerRaceHistory.js"; -import { _getStockCarInfo } from "./_getStockCarInfo.js"; -import { _getTunables } from "./_getTunables.js"; -import { _logout } from "./_logout.js"; -import { clientConnect } from "./clientConnect.js"; -import { getLobbies } from "./getLobbies.js"; -import { login } from "./login.js"; -import { trackingPing } from "./trackingPing.js"; -import { _buyCarFromDealer } from "./_buyCarFromDealer.js"; -import { IServerMessage } from "rusty-motors-shared-packets"; +import { ServerLogger } from 'rusty-motors-logger'; +import { _getArcadeCarInfo } from './_getArcadeCarInfo.js'; +import { _getGameUrls } from './_getGameUrls.js'; +import { _getOwnedParts } from './_getOwnedParts.js'; +import { _getOwnedVehicles } from './_getOwnedVehicles.js'; +import { _getPlayerInfo } from './_getPlayerInfo.js'; +import { _getPlayerPhysical } from './_getPlayerPhysical.js'; +import { _getPlayerRaceHistory } from './_getPlayerRaceHistory.js'; +import { _getStockCarInfo } from './_getStockCarInfo.js'; +import { _getTunables } from './_getTunables.js'; +import { _logout } from './_logout.js'; +import { clientConnect } from './clientConnect.js'; +import { getLobbies } from './getLobbies.js'; +import { login } from './login.js'; +import { trackingPing } from './trackingPing.js'; +import { _buyCarFromDealer } from './_buyCarFromDealer.js'; +import { IServerMessage } from 'rusty-motors-shared-packets'; export interface MessageHandlerArgs { - connectionId: string; - packet: IServerMessage; - log?: ServerLogger; + connectionId: string; + packet: IServerMessage; + log?: ServerLogger; } export interface MessageHandlerResult { - connectionId: string; - messages: IServerMessage[]; + connectionId: string; + messages: IServerMessage[]; } export interface MessageHandler { - name: string; - handler: (args: MessageHandlerArgs) => Promise; + name: string; + handler: (args: MessageHandlerArgs) => Promise; } export const messageHandlers: MessageHandler[] = [ - { - name: "MC_TRACKING_MSG", - handler: trackingPing, - }, - { - name: "MC_CLIENT_CONNECT_MSG", - handler: clientConnect, - }, - { - name: "MC_LOGIN", - handler: login, - }, - { - name: "MC_LOGOUT", - handler: _logout, - }, - // { - // name: "MC_GET_COMPLETE_VEHICLE_INFO", - // handler: _getFullCarInfo, - // } - { - name: "MC_GET_LOBBIES", - handler: getLobbies, - }, - { - name: "MC_STOCK_CAR_INFO", - handler: _getStockCarInfo, - }, - { - name: "MC_GET_ARCADE_CARS", - handler: _getArcadeCarInfo, - }, - { - name: "MC_GET_GAME_URLS", - handler: _getGameUrls, - }, - { - name: "MC_GET_MCO_TUNABLES", - handler: _getTunables, - }, - { - name: "MC_GET_OWNED_VEHICLES", - handler: _getOwnedVehicles, - }, - { - name: "MC_GET_PLAYER_INFO", - handler: _getPlayerInfo, - }, - { - name: "MC_GET_PLAYER_PHYSICAL", - handler: _getPlayerPhysical, - }, - { - name: "MC_GET_OWNED_PARTS", - handler: _getOwnedParts, - }, - { - name: "MC_GET_PLAYER_RACING_HISTORY", - handler: _getPlayerRaceHistory, - }, - { - name: "MC_PURCHASE_STOCK_CAR", - handler: _buyCarFromDealer, - } + { + name: 'MC_TRACKING_MSG', + handler: trackingPing, + }, + { + name: 'MC_CLIENT_CONNECT_MSG', + handler: clientConnect, + }, + { + name: 'MC_LOGIN', + handler: login, + }, + { + name: 'MC_LOGOUT', + handler: _logout, + }, + // { + // name: "MC_GET_COMPLETE_VEHICLE_INFO", + // handler: _getFullCarInfo, + // } + { + name: 'MC_GET_LOBBIES', + handler: getLobbies, + }, + { + name: 'MC_STOCK_CAR_INFO', + handler: _getStockCarInfo, + }, + { + name: 'MC_GET_ARCADE_CARS', + handler: _getArcadeCarInfo, + }, + { + name: 'MC_GET_GAME_URLS', + handler: _getGameUrls, + }, + { + name: 'MC_GET_MCO_TUNABLES', + handler: _getTunables, + }, + { + name: 'MC_GET_OWNED_VEHICLES', + handler: _getOwnedVehicles, + }, + { + name: 'MC_GET_PLAYER_INFO', + handler: _getPlayerInfo, + }, + { + name: 'MC_GET_PLAYER_PHYSICAL', + handler: _getPlayerPhysical, + }, + { + name: 'MC_GET_OWNED_PARTS', + handler: _getOwnedParts, + }, + { + name: 'MC_GET_PLAYER_RACING_HISTORY', + handler: _getPlayerRaceHistory, + }, + { + name: 'MC_PURCHASE_STOCK_CAR', + handler: _buyCarFromDealer, + }, ]; diff --git a/packages/transactions/src/internal.ts b/packages/transactions/src/internal.ts index bd0729548..c90dc024b 100644 --- a/packages/transactions/src/internal.ts +++ b/packages/transactions/src/internal.ts @@ -14,28 +14,25 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { getServerLogger, ServerLogger } from "rusty-motors-shared"; - +import { getServerLogger, ServerLogger } from 'rusty-motors-logger'; import { - McosEncryption, - SerializedBufferOld, - type State, -} from "rusty-motors-shared"; + McosEncryption, + SerializedBufferOld, + type State, +} from 'rusty-motors-shared'; import { - fetchStateFromDatabase, - getEncryption, - updateEncryption, -} from "rusty-motors-shared"; -import { OldServerMessage } from "rusty-motors-shared"; -import { messageHandlers, type MessageHandlerResult } from "./handlers.js"; + fetchStateFromDatabase, + getEncryption, + updateEncryption, +} from 'rusty-motors-shared'; +import { OldServerMessage } from 'rusty-motors-shared'; +import { messageHandlers, type MessageHandlerResult } from './handlers.js'; import { - ServerPacket, - type BufferSerializer, -} from "rusty-motors-shared-packets"; -import { _MSG_STRING } from "./_MSG_STRING.js"; - - + ServerPacket, + type BufferSerializer, +} from 'rusty-motors-shared-packets'; +import { _MSG_STRING } from './_MSG_STRING.js'; /** * Route or process MCOTS commands @@ -43,47 +40,50 @@ import { _MSG_STRING } from "./_MSG_STRING.js"; * @returns {Promise} */ async function processInput({ - connectionId, - inboundMessage, - log = getServerLogger( "transactionServer.processInput"), + connectionId, + inboundMessage, + log = getServerLogger('transactionServer.processInput'), }: { - connectionId: string; - inboundMessage: ServerPacket; - log?: ServerLogger; + connectionId: string; + inboundMessage: ServerPacket; + log?: ServerLogger; }): Promise { - const currentMessageNo = inboundMessage.getMessageId(); - const currentMessageString = _MSG_STRING(currentMessageNo); - - log.debug( - `[${connectionId}] Processing message: ${currentMessageNo} (${currentMessageString}), sequence: ${inboundMessage.getSequence()}`, - ); - - const result = messageHandlers.find( - (msg) => msg.name === currentMessageString, - ); - - if (typeof result !== "undefined") { - // Turn this into an OldServerMessage for compatibility - const packet = new OldServerMessage(); - packet._doDeserialize(inboundMessage.serialize()); - - try { - const responsePackets = await result.handler({ - connectionId, - packet, - }); - return responsePackets; - } catch (error) { - const err = Error(`[${connectionId}] Error processing message ${error}`, { - cause: error, - }); - throw err; - } - } - - throw Error( - `[${connectionId}] Unable to locate handler for message: ${currentMessageNo} (${currentMessageString})`, - ); + const currentMessageNo = inboundMessage.getMessageId(); + const currentMessageString = _MSG_STRING(currentMessageNo); + + log.debug( + `[${connectionId}] Processing message: ${currentMessageNo} (${currentMessageString}), sequence: ${inboundMessage.getSequence()}`, + ); + + const result = messageHandlers.find( + (msg) => msg.name === currentMessageString, + ); + + if (typeof result !== 'undefined') { + // Turn this into an OldServerMessage for compatibility + const packet = new OldServerMessage(); + packet._doDeserialize(inboundMessage.serialize()); + + try { + const responsePackets = await result.handler({ + connectionId, + packet, + }); + return responsePackets; + } catch (error) { + const err = Error( + `[${connectionId}] Error processing message ${error}`, + { + cause: error, + }, + ); + throw err; + } + } + + throw Error( + `[${connectionId}] Unable to locate handler for message: ${currentMessageNo} (${currentMessageString})`, + ); } /** @@ -97,118 +97,121 @@ async function processInput({ * }>} */ export async function receiveTransactionsData({ - connectionId, - message, - log = getServerLogger( "transactionServer.receiveTransactionsData"), + connectionId, + message, + log = getServerLogger('transactionServer.receiveTransactionsData'), }: { - connectionId: string; - message: BufferSerializer; - log?: ServerLogger; + connectionId: string; + message: BufferSerializer; + log?: ServerLogger; }): Promise<{ - connectionId: string; - messages: SerializedBufferOld[]; + connectionId: string; + messages: SerializedBufferOld[]; }> { - - // Normalize the message - - const inboundMessage = new ServerPacket(); - inboundMessage.deserialize(message.serialize()); - - log.debug( - `[${connectionId}] Received message: ${inboundMessage.toHexString()}`, - ); - - let decryptedMessage: ServerPacket; - - // Is the message encrypted? - if (inboundMessage.isPayloadEncrypted()) { - log.debug(`[${connectionId}] Message is encrypted`); - // Get the encryyption settings for this connection - const state = fetchStateFromDatabase(); - - const encryptionSettings = getEncryption(state, connectionId); - - if (typeof encryptionSettings === "undefined") { - throw Error(`[${connectionId}] Unable to locate encryption settings`); - } - - // log the old buffer - log.debug( - `[${connectionId}] Inbound buffer: ${inboundMessage.data.toHexString()}`, - ); - - decryptedMessage = decryptMessage( - encryptionSettings, - inboundMessage, - state, - connectionId, - ); - } else { - log.debug(`[${connectionId}] Message is not encrypted`); - decryptedMessage = inboundMessage; - } - - // Process the message - - const response = await processInput({ - connectionId, - inboundMessage: decryptedMessage, - log, - }); - - // Loop through the outbound messages and encrypt them - const outboundMessages: ServerPacket[] = []; - - response.messages.forEach((message) => { - log.debug(`[${connectionId}] Processing outbound message`); - - const outboundMessage = new ServerPacket(); - outboundMessage.deserialize(message.serialize()); - - if (outboundMessage.isPayloadEncrypted()) { - const state = fetchStateFromDatabase(); - - const encryptionSettings = getEncryption(state, connectionId); - - if (typeof encryptionSettings === "undefined") { - throw Error(`[${connectionId}] Unable to locate encryption settings`); - } - - // log the old buffer - log.debug( - `[${connectionId}] Outbound buffer: ${outboundMessage.data.toHexString()}`, - ); - - const encryptedMessage = encryptOutboundMessage( - encryptionSettings, - outboundMessage, - state, - connectionId, - ); - outboundMessages.push(encryptedMessage); - } else { - log.debug( - `[${connectionId}] Outbound message: ${outboundMessage.toHexString()}`, - ); - outboundMessages.push(outboundMessage); - } - }); - - log.debug( - `[${connectionId}] Exiting transaction module with ${outboundMessages.length} messages`, - ); - - // Convert the outbound messages to SerializedBufferOld - const outboundMessagesSerialized = outboundMessages.map((message) => { - const serialized = new SerializedBufferOld(); - serialized._doDeserialize(message.serialize()); - return serialized; - }); - - return { - connectionId, - messages: outboundMessagesSerialized, - }; + // Normalize the message + + const inboundMessage = new ServerPacket(); + inboundMessage.deserialize(message.serialize()); + + log.debug( + `[${connectionId}] Received message: ${inboundMessage.toHexString()}`, + ); + + let decryptedMessage: ServerPacket; + + // Is the message encrypted? + if (inboundMessage.isPayloadEncrypted()) { + log.debug(`[${connectionId}] Message is encrypted`); + // Get the encryyption settings for this connection + const state = fetchStateFromDatabase(); + + const encryptionSettings = getEncryption(state, connectionId); + + if (typeof encryptionSettings === 'undefined') { + throw Error( + `[${connectionId}] Unable to locate encryption settings`, + ); + } + + // log the old buffer + log.debug( + `[${connectionId}] Inbound buffer: ${inboundMessage.data.toHexString()}`, + ); + + decryptedMessage = decryptMessage( + encryptionSettings, + inboundMessage, + state, + connectionId, + ); + } else { + log.debug(`[${connectionId}] Message is not encrypted`); + decryptedMessage = inboundMessage; + } + + // Process the message + + const response = await processInput({ + connectionId, + inboundMessage: decryptedMessage, + log, + }); + + // Loop through the outbound messages and encrypt them + const outboundMessages: ServerPacket[] = []; + + response.messages.forEach((message) => { + log.debug(`[${connectionId}] Processing outbound message`); + + const outboundMessage = new ServerPacket(); + outboundMessage.deserialize(message.serialize()); + + if (outboundMessage.isPayloadEncrypted()) { + const state = fetchStateFromDatabase(); + + const encryptionSettings = getEncryption(state, connectionId); + + if (typeof encryptionSettings === 'undefined') { + throw Error( + `[${connectionId}] Unable to locate encryption settings`, + ); + } + + // log the old buffer + log.debug( + `[${connectionId}] Outbound buffer: ${outboundMessage.data.toHexString()}`, + ); + + const encryptedMessage = encryptOutboundMessage( + encryptionSettings, + outboundMessage, + state, + connectionId, + ); + outboundMessages.push(encryptedMessage); + } else { + log.debug( + `[${connectionId}] Outbound message: ${outboundMessage.toHexString()}`, + ); + outboundMessages.push(outboundMessage); + } + }); + + log.debug( + `[${connectionId}] Exiting transaction module with ${outboundMessages.length} messages`, + ); + + // Convert the outbound messages to SerializedBufferOld + const outboundMessagesSerialized = outboundMessages.map((message) => { + const serialized = new SerializedBufferOld(); + serialized._doDeserialize(message.serialize()); + return serialized; + }); + + return { + connectionId, + messages: outboundMessagesSerialized, + }; } /** @@ -223,80 +226,83 @@ export async function receiveTransactionsData({ * @throws Will throw an error if the message cannot be decrypted. */ function decryptMessage( - encryptionSettings: McosEncryption, - inboundMessage: ServerPacket, - state: State, - connectionId: string, - log: ServerLogger = getServerLogger("transactionServer.decryptMessage"), + encryptionSettings: McosEncryption, + inboundMessage: ServerPacket, + state: State, + connectionId: string, + log: ServerLogger = getServerLogger('transactionServer.decryptMessage'), ): ServerPacket { - try { - const decryptedMessage = encryptionSettings.dataEncryption.decrypt( - inboundMessage.data.serialize(), - ); - updateEncryption(state, encryptionSettings).save(); - - // Verify the length of the message - verifyLength(inboundMessage.data.serialize(), decryptedMessage); - - // Assuming the message was decrypted successfully, update the buffer - log.debug( - `[${connectionId}] Decrypted buffer: ${decryptedMessage.toString("hex")}`, - ); - - const outboundMessage = ServerPacket.copy(inboundMessage, decryptedMessage); - outboundMessage.setPayloadEncryption(false); - - log.debug( - `[${connectionId}] Decrypted message: ${inboundMessage.toHexString()}`, - ); - - return outboundMessage; - } catch (error) { - const err = Error(`[${connectionId}] Unable to decrypt message`, { - cause: error, - }); - throw err; - } + try { + const decryptedMessage = encryptionSettings.dataEncryption.decrypt( + inboundMessage.data.serialize(), + ); + updateEncryption(state, encryptionSettings).save(); + + // Verify the length of the message + verifyLength(inboundMessage.data.serialize(), decryptedMessage); + + // Assuming the message was decrypted successfully, update the buffer + log.debug( + `[${connectionId}] Decrypted buffer: ${decryptedMessage.toString('hex')}`, + ); + + const outboundMessage = ServerPacket.copy( + inboundMessage, + decryptedMessage, + ); + outboundMessage.setPayloadEncryption(false); + + log.debug( + `[${connectionId}] Decrypted message: ${inboundMessage.toHexString()}`, + ); + + return outboundMessage; + } catch (error) { + const err = Error(`[${connectionId}] Unable to decrypt message`, { + cause: error, + }); + throw err; + } } function encryptOutboundMessage( - encryptionSettings: McosEncryption, - unencryptedMessage: ServerPacket, - state: State, - connectionId: string, - log = getServerLogger("transactionServer.encryptOutboundMessage"), + encryptionSettings: McosEncryption, + unencryptedMessage: ServerPacket, + state: State, + connectionId: string, + log = getServerLogger('transactionServer.encryptOutboundMessage'), ): ServerPacket { - try { - const encryptedMessage = encryptionSettings.dataEncryption.encrypt( - unencryptedMessage.data.serialize(), - ); - updateEncryption(state, encryptionSettings).save(); - - // Verify the length of the message - verifyLength(unencryptedMessage.data.serialize(), encryptedMessage); - - // Assuming the message was decrypted successfully, update the buffer - log.debug( - `[${connectionId}] Encrypted buffer: ${encryptedMessage.toString("hex")}`, - ); - - const outboundMessage = ServerPacket.copy( - unencryptedMessage, - encryptedMessage, - ); - outboundMessage.setPayloadEncryption(true); - - log.debug( - `[${connectionId}] Encrypted message: ${outboundMessage.toHexString()}`, - ); - - return outboundMessage; - } catch (error) { - const err = Error(`[${connectionId}] Unable to encrypt message`, { - cause: error, - }); - throw err; - } + try { + const encryptedMessage = encryptionSettings.dataEncryption.encrypt( + unencryptedMessage.data.serialize(), + ); + updateEncryption(state, encryptionSettings).save(); + + // Verify the length of the message + verifyLength(unencryptedMessage.data.serialize(), encryptedMessage); + + // Assuming the message was decrypted successfully, update the buffer + log.debug( + `[${connectionId}] Encrypted buffer: ${encryptedMessage.toString('hex')}`, + ); + + const outboundMessage = ServerPacket.copy( + unencryptedMessage, + encryptedMessage, + ); + outboundMessage.setPayloadEncryption(true); + + log.debug( + `[${connectionId}] Encrypted message: ${outboundMessage.toHexString()}`, + ); + + return outboundMessage; + } catch (error) { + const err = Error(`[${connectionId}] Unable to encrypt message`, { + cause: error, + }); + throw err; + } } /** @@ -304,7 +310,7 @@ function encryptOutboundMessage( * @param {Buffer} buffer2 */ export function verifyLength(buffer: Buffer, buffer2: Buffer) { - if (buffer.length !== buffer2.length) { - throw Error(`Length mismatch: ${buffer.length} !== ${buffer2.length}`); - } + if (buffer.length !== buffer2.length) { + throw Error(`Length mismatch: ${buffer.length} !== ${buffer2.length}`); + } } diff --git a/packages/transactions/src/login.ts b/packages/transactions/src/login.ts index c3d5ddd1e..c2c3b41bf 100644 --- a/packages/transactions/src/login.ts +++ b/packages/transactions/src/login.ts @@ -1,72 +1,77 @@ -import { OldServerMessage } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; import { - LoginCompletePayload, - LoginPayload, - ServerPacket, -} from "rusty-motors-shared-packets"; -import { getServerLogger } from "rusty-motors-shared"; + LoginCompletePayload, + LoginPayload, + ServerPacket, +} from 'rusty-motors-shared-packets'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/login"); +const defaultLogger = getServerLogger('handlers/login'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function login({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - // Normalize the packet - const incomingPacket = new ServerPacket(); - incomingPacket.deserialize(packet.serialize()); + // Normalize the packet + const incomingPacket = new ServerPacket(); + incomingPacket.deserialize(packet.serialize()); - log.debug( - `[${connectionId}] Received LoginMessage: ${incomingPacket.toString()}`, - ); + log.debug( + `[${connectionId}] Received LoginMessage: ${incomingPacket.toString()}`, + ); - // Read the inbound packet - const loginMessage = new LoginPayload(); - loginMessage.deserialize(packet.data); - log.debug( - `[${connectionId}] Received LoginMessage: ${loginMessage.toString()}`, - ); + // Read the inbound packet + const loginMessage = new LoginPayload(); + loginMessage.deserialize(packet.data); + log.debug( + `[${connectionId}] Received LoginMessage: ${loginMessage.toString()}`, + ); - // Create new response packet - const response = new LoginCompletePayload(); - response.setMessageId(213); - response.serverTime = Math.floor(Date.now() / 1000); - response.firstTime = true; - response.clubInvitesWaiting = false; - response.paycheckWaiting = true; - response.shardAverageCarsOwned = 3; - response.shardAveragePlayerLevel = 5; - response.shardGNP = 830; + // Create new response packet + const response = new LoginCompletePayload(); + response.setMessageId(213); + response.serverTime = Math.floor(Date.now() / 1000); + response.firstTime = true; + response.clubInvitesWaiting = false; + response.paycheckWaiting = true; + response.shardAverageCarsOwned = 3; + response.shardAveragePlayerLevel = 5; + response.shardGNP = 830; - log.debug( - `[${connectionId}] Sending LoginCompleteMessage: ${response.toString()}`, - ); + log.debug( + `[${connectionId}] Sending LoginCompleteMessage: ${response.toString()}`, + ); - // Send response packet + // Send response packet - // Normalize the packet + // Normalize the packet - const outgoingPacket = ServerPacket.copy(incomingPacket, response.serialize()); - outgoingPacket.setSequence(incomingPacket.getSequence()); - outgoingPacket.setPayloadEncryption(true); - outgoingPacket.setSignature("TOMC"); + const outgoingPacket = ServerPacket.copy( + incomingPacket, + response.serialize(), + ); + outgoingPacket.setSequence(incomingPacket.getSequence()); + outgoingPacket.setPayloadEncryption(true); + outgoingPacket.setSignature('TOMC'); - log.debug(`[${connectionId}] Sending response: ${outgoingPacket.toString()}`); + log.debug( + `[${connectionId}] Sending response: ${outgoingPacket.toString()}`, + ); - log.debug( - `[${connectionId}] Sending response(hex): ${outgoingPacket.serialize().toString("hex")}`, - ); + log.debug( + `[${connectionId}] Sending response(hex): ${outgoingPacket.serialize().toString('hex')}`, + ); - const responsePacket = new OldServerMessage(); - responsePacket._header.sequence = incomingPacket.getSequence(); - responsePacket._doDeserialize(outgoingPacket.serialize()); + const responsePacket = new OldServerMessage(); + responsePacket._header.sequence = incomingPacket.getSequence(); + responsePacket._doDeserialize(outgoingPacket.serialize()); - return { connectionId, messages: [responsePacket] }; + return { connectionId, messages: [responsePacket] }; } diff --git a/packages/transactions/src/trackingPing.ts b/packages/transactions/src/trackingPing.ts index 700de36ef..ca19e6665 100644 --- a/packages/transactions/src/trackingPing.ts +++ b/packages/transactions/src/trackingPing.ts @@ -1,30 +1,30 @@ -import { OldServerMessage } from "rusty-motors-shared"; -import { GenericReplyMessage } from "./GenericReplyMessage.js"; -import type { MessageHandlerArgs, MessageHandlerResult } from "./handlers.js"; -import { getServerLogger } from "rusty-motors-shared"; +import { OldServerMessage } from 'rusty-motors-shared'; +import { GenericReplyMessage } from './GenericReplyMessage.js'; +import type { MessageHandlerArgs, MessageHandlerResult } from './handlers.js'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("handlers/trackingPing"); +const defaultLogger = getServerLogger('handlers/trackingPing'); /** * @param {MessageHandlerArgs} args * @return {Promise} */ export async function trackingPing({ - connectionId, - packet, - log = defaultLogger, + connectionId, + packet, + log = defaultLogger, }: MessageHandlerArgs): Promise { - // Create new response packet - const pReply = new GenericReplyMessage(); - pReply.msgNo = 101; - pReply.msgReply = 440; - const rPacket = new OldServerMessage(); - rPacket._header.sequence = packet.sequenceNumber; - rPacket._header.flags = 8; + // Create new response packet + const pReply = new GenericReplyMessage(); + pReply.msgNo = 101; + pReply.msgReply = 440; + const rPacket = new OldServerMessage(); + rPacket._header.sequence = packet.sequenceNumber; + rPacket._header.flags = 8; - rPacket.setBuffer(pReply.serialize()); + rPacket.setBuffer(pReply.serialize()); - log.debug(`TrackingPing: ${rPacket.toString()}`); + log.debug(`TrackingPing: ${rPacket.toString()}`); - return { connectionId, messages: [] }; + return { connectionId, messages: [] }; } diff --git a/packages/transactions/test/clientConnect.test.ts b/packages/transactions/test/clientConnect.test.ts index f1fc929c9..fd433fd72 100644 --- a/packages/transactions/test/clientConnect.test.ts +++ b/packages/transactions/test/clientConnect.test.ts @@ -1,10 +1,11 @@ import { describe, expect, it, vi } from "vitest"; import { TClientConnectMessage } from "../src/TClientConnectMessage.js"; import { clientConnect } from "../src/clientConnect.js"; -import { ConnectionRecord, ServerLogger } from "rusty-motors-shared"; +import { ConnectionRecord, } from "rusty-motors-shared"; +import { ServerLogger } from "rusty-motors-logger"; vi.mock("rusty-motors-database", () => ({ - databaseManager: { + databaseService: { updateSessionKey: vi.fn(), fetchSessionKeyByConnectionId: vi.fn(), fetchSessionKeyByCustomerId: vi.fn().mockImplementation(() => { @@ -16,7 +17,7 @@ vi.mock("rusty-motors-database", () => ({ const mockDatabaseManager = vi.mocked( await import("rusty-motors-database"), -).databaseManager; +).databaseService; describe("clientConnect", () => { it("throws when connection is not found", async () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ab749e17..c35f30d00 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - tinymce: ^7.9.0 - importers: .: @@ -40,7 +37,7 @@ importers: version: 9.19.0 adminjs: specifier: ^7.8.15 - version: 7.8.15(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@types/babel__core@7.20.5)(@types/react@19.0.4)(debug@4.4.1) + version: 7.8.15(@types/babel__core@7.20.5)(@types/react@19.1.4)(debug@4.4.1) bcrypt: specifier: ^6.0.0 version: 6.0.0 @@ -71,6 +68,9 @@ importers: rusty-motors-lobby: specifier: link:packages/lobby version: link:packages/lobby + rusty-motors-logger: + specifier: workspace:^ + version: link:packages/logger rusty-motors-login: specifier: link:packages/login version: link:packages/login @@ -94,7 +94,7 @@ importers: version: link:packages/transactions ts-node: specifier: 10.9.2 - version: 10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3) + version: 10.9.2(@swc/core@1.11.24(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3) devDependencies: '@biomejs/biome': specifier: 1.9.4 @@ -114,36 +114,15 @@ importers: '@databases/pg-test': specifier: ^3.1.2 version: 3.1.2(typescript@5.8.3) - '@eslint/eslintrc': - specifier: ^3.3.1 - version: 3.3.1 - '@eslint/js': - specifier: ^9.27.0 - version: 9.27.0 - '@nx/eslint': - specifier: 21.0.3 - version: 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@zkochan/js-yaml@0.0.7)(debug@4.4.1)(eslint@9.27.0(jiti@2.4.2))(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/js': - specifier: 21.0.3 - version: 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/node': - specifier: 21.0.3 - version: 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(@zkochan/js-yaml@0.0.7)(babel-plugin-macros@3.1.0)(debug@4.4.1)(eslint@9.27.0(jiti@2.4.2))(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))(ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3))(typescript@5.8.3) - '@nx/vite': - specifier: 21.0.3 - version: 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1))(vitest@3.1.3) - '@nx/web': - specifier: 21.0.3 - version: 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) '@sentry/cli': specifier: ^2.45.0 version: 2.45.0 '@swc-node/register': specifier: ~1.10.10 - version: 1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3) + version: 1.10.10(@swc/core@1.11.24(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3) '@swc/core': - specifier: ~1.11.22 - version: 1.11.22(@swc/helpers@0.5.17) + specifier: ~1.11.24 + version: 1.11.24(@swc/helpers@0.5.17) '@swc/helpers': specifier: ~0.5.17 version: 0.5.17 @@ -168,27 +147,12 @@ importers: '@types/sinon-chai': specifier: 4.0.0 version: 4.0.0 - '@typescript-eslint/eslint-plugin': - specifier: ^8.32.1 - version: 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': - specifier: ^8.32.1 - version: 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@vitest/coverage-v8': specifier: ^3.1.3 version: 3.1.3(vitest@3.1.3) '@vitest/ui': specifier: ^3.1.3 version: 3.1.3(vitest@3.1.3) - eslint: - specifier: ^9.27.0 - version: 9.27.0(jiti@2.4.2) - eslint-config-prettier: - specifier: ^10.1.5 - version: 10.1.5(eslint@9.27.0(jiti@2.4.2)) - eslint-plugin-prettier: - specifier: 5.4.0 - version: 5.4.0(eslint-config-prettier@10.1.5(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(prettier@3.5.3) globals: specifier: ^16.1.0 version: 16.1.0 @@ -198,15 +162,6 @@ importers: lint-staged: specifier: ^16.0.0 version: 16.0.0 - nx: - specifier: 21.0.3 - version: 21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1) - prettier: - specifier: 3.5.3 - version: 3.5.3 - prettier-eslint: - specifier: 16.4.2 - version: 16.4.2(typescript@5.8.3) rimraf: specifier: ^6.0.1 version: 6.0.1 @@ -222,9 +177,6 @@ importers: typescript: specifier: ^5.8.3 version: 5.8.3 - typescript-eslint-language-service: - specifier: ^5.0.5 - version: 5.0.5(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) vite: specifier: ^6.3.5 version: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) @@ -309,6 +261,9 @@ importers: fastify: specifier: ^5.3.3 version: 5.3.3 + rusty-motors-logger: + specifier: workspace:^ + version: link:../logger rusty-motors-shared: specifier: workspace:1.0.0-next.0 version: link:../shared @@ -320,6 +275,28 @@ importers: specifier: ^3.1.3 version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) + packages/logger: + dependencies: + fastify: + specifier: ^5.3.2 + version: 5.3.3 + pino: + specifier: ^9.6.0 + version: 9.6.0 + pino-pretty: + specifier: ^13.0.0 + version: 13.0.0 + rusty-motors-shared-packets: + specifier: workspace:1.0.0-next.0 + version: link:../shared-packets + devDependencies: + '@vitest/coverage-v8': + specifier: 3.1.3 + version: 3.1.3(vitest@3.1.3) + vitest: + specifier: ^3.1.3 + version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) + packages/login: dependencies: '@fastify/sensible': @@ -360,6 +337,9 @@ importers: '@sentry/profiling-node': specifier: 9.19.0 version: 9.19.0 + rusty-motors-logger: + specifier: workspace:^ + version: link:../logger short-unique-id: specifier: ^5.3.2 version: 5.3.2 @@ -386,6 +366,12 @@ importers: packages/shared: dependencies: + config: + specifier: ^4.0.0 + version: 4.0.0 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 fastify: specifier: ^5.3.3 version: 5.3.3 @@ -395,10 +381,16 @@ importers: pino-pretty: specifier: ^13.0.0 version: 13.0.0 + rusty-motors-logger: + specifier: workspace:^ + version: link:../logger rusty-motors-shared-packets: specifier: workspace:1.0.0-next.0 version: link:../shared-packets devDependencies: + '@types/config': + specifier: ^3.3.5 + version: 3.3.5 '@vitest/coverage-v8': specifier: 3.1.3 version: 3.1.3(vitest@3.1.3) @@ -424,6 +416,9 @@ importers: '@sentry/profiling-node': specifier: 9.19.0 version: 9.19.0 + rusty-motors-logger: + specifier: workspace:^ + version: link:../logger devDependencies: '@vitest/coverage-v8': specifier: 3.1.3 @@ -454,263 +449,166 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.25.7': - resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/compat-data@7.27.2': + resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.5': - resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} + '@babel/core@7.27.1': + resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.0': - resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + '@babel/generator@7.27.1': + resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.5': - resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} + '@babel/helper-annotate-as-pure@7.27.1': + resolution: {integrity: sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.25.9': - resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.26.5': - resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.25.9': - resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} + '@babel/helper-create-class-features-plugin@7.27.1': + resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.26.3': - resolution: {integrity: sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==} + '@babel/helper-create-regexp-features-plugin@7.27.1': + resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.3': - resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==} + '@babel/helper-define-polyfill-provider@0.6.4': + resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - '@babel/helper-member-expression-to-functions@7.25.9': - resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + '@babel/helper-module-transforms@7.27.1': + resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.25.9': - resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.26.5': - resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-remap-async-to-generator@7.25.9': - resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.26.5': - resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==} + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.7': - resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.25.9': - resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} + '@babel/helper-wrap-function@7.27.1': + resolution: {integrity: sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + '@babel/helpers@7.27.1': + resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.25.7': - resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.26.5': - resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} + '@babel/parser@7.27.2': + resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': - resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': + resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9': - resolution: {integrity: sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==} + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9': - resolution: {integrity: sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9': - resolution: {integrity: sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9': - resolution: {integrity: sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': + resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-proposal-decorators@7.25.9': - resolution: {integrity: sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-decorators@7.25.9': - resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-assertions@7.26.0': - resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.26.0': - resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.25.9': - resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -721,344 +619,344 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-arrow-functions@7.25.9': - resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==} + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.25.9': - resolution: {integrity: sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==} + '@babel/plugin-transform-async-generator-functions@7.27.1': + resolution: {integrity: sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.25.9': - resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==} + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoped-functions@7.26.5': - resolution: {integrity: sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==} + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.25.9': - resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==} + '@babel/plugin-transform-block-scoping@7.27.1': + resolution: {integrity: sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.25.9': - resolution: {integrity: sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==} + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.26.0': - resolution: {integrity: sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==} + '@babel/plugin-transform-class-static-block@7.27.1': + resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.25.9': - resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==} + '@babel/plugin-transform-classes@7.27.1': + resolution: {integrity: sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.25.9': - resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==} + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.25.9': - resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==} + '@babel/plugin-transform-destructuring@7.27.1': + resolution: {integrity: sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dotall-regex@7.25.9': - resolution: {integrity: sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==} + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-keys@7.25.9': - resolution: {integrity: sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==} + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9': - resolution: {integrity: sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-dynamic-import@7.25.9': - resolution: {integrity: sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==} + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.26.3': - resolution: {integrity: sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==} + '@babel/plugin-transform-exponentiation-operator@7.27.1': + resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-export-namespace-from@7.25.9': - resolution: {integrity: sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==} + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-for-of@7.25.9': - resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==} + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.25.9': - resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==} + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-json-strings@7.25.9': - resolution: {integrity: sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==} + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-literals@7.25.9': - resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==} + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.25.9': - resolution: {integrity: sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==} + '@babel/plugin-transform-logical-assignment-operators@7.27.1': + resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-member-expression-literals@7.25.9': - resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==} + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-amd@7.25.9': - resolution: {integrity: sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==} + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.26.3': - resolution: {integrity: sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==} + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.25.9': - resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==} + '@babel/plugin-transform-modules-systemjs@7.27.1': + resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-umd@7.25.9': - resolution: {integrity: sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==} + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-named-capturing-groups-regex@7.25.9': - resolution: {integrity: sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==} + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-new-target@7.25.9': - resolution: {integrity: sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==} + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.26.5': - resolution: {integrity: sha512-OHqczNm4NTQlW1ghrVY43FPoiRzbmzNVbcgVnMKZN/RQYezHUSdjACjaX50CD3B7UIAjv39+MlsrVDb3v741FA==} + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.25.9': - resolution: {integrity: sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==} + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.25.9': - resolution: {integrity: sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==} + '@babel/plugin-transform-object-rest-spread@7.27.2': + resolution: {integrity: sha512-AIUHD7xJ1mCrj3uPozvtngY3s0xpv7Nu7DoUSnzNY6Xam1Cy4rUznR//pvMHOhQ4AvbCexhbqXCtpxGHOGOO6g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-super@7.25.9': - resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==} + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.25.9': - resolution: {integrity: sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==} + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.25.9': - resolution: {integrity: sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==} + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.25.9': - resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==} + '@babel/plugin-transform-parameters@7.27.1': + resolution: {integrity: sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.25.9': - resolution: {integrity: sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==} + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.25.9': - resolution: {integrity: sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==} + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-property-literals@7.25.9': - resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==} + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-display-name@7.25.9': - resolution: {integrity: sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==} + '@babel/plugin-transform-react-display-name@7.27.1': + resolution: {integrity: sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-development@7.25.9': - resolution: {integrity: sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==} + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.25.9': - resolution: {integrity: sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==} + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-pure-annotations@7.25.9': - resolution: {integrity: sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==} + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.25.9': - resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==} + '@babel/plugin-transform-regenerator@7.27.1': + resolution: {integrity: sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regexp-modifiers@7.26.0': - resolution: {integrity: sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==} + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-reserved-words@7.25.9': - resolution: {integrity: sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==} + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-runtime@7.25.9': - resolution: {integrity: sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==} + '@babel/plugin-transform-runtime@7.27.1': + resolution: {integrity: sha512-TqGF3desVsTcp3WrJGj4HfKokfCXCLcHpt4PJF0D8/iT6LPd9RS82Upw3KPeyr6B22Lfd3DO8MVrmp0oRkUDdw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-shorthand-properties@7.25.9': - resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==} + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.25.9': - resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==} + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-sticky-regex@7.25.9': - resolution: {integrity: sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==} + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-template-literals@7.25.9': - resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==} + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typeof-symbol@7.25.9': - resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==} + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.26.5': - resolution: {integrity: sha512-GJhPO0y8SD5EYVCy2Zr+9dSZcEgaSmq5BLR0Oc25TOEhC+ba49vUAGZFjy8v79z9E1mdldq4x9d1xgh4L1d5dQ==} + '@babel/plugin-transform-typescript@7.27.1': + resolution: {integrity: sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-escapes@7.25.9': - resolution: {integrity: sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==} + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-property-regex@7.25.9': - resolution: {integrity: sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==} + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-regex@7.25.9': - resolution: {integrity: sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==} + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-sets-regex@7.25.9': - resolution: {integrity: sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==} + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.26.0': - resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} + '@babel/preset-env@7.27.2': + resolution: {integrity: sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1068,43 +966,40 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/preset-react@7.26.3': - resolution: {integrity: sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==} + '@babel/preset-react@7.27.1': + resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.26.0': - resolution: {integrity: sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==} + '@babel/preset-typescript@7.27.1': + resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/register@7.25.9': - resolution: {integrity: sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA==} + '@babel/register@7.27.1': + resolution: {integrity: sha512-K13lQpoV54LATKkzBpBAEu1GGSIRzxR9f4IN4V8DCDgiUMo2UDGagEZr3lPeVNJPLkWUi5JE4hCHKneVTwQlYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.0': - resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + '@babel/runtime@7.27.1': + resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} engines: {node: '>=6.9.0'} - '@babel/template@7.25.9': - resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.5': - resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} + '@babel/traverse@7.27.1': + resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.5': - resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} + '@babel/types@7.27.1': + resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -1312,20 +1207,20 @@ packages: resolution: {integrity: sha512-18Aa+7KP/L2Kj9lxmT4EJZnsCq/xGIHgzU26rdzsKMhjpeT3YY+qin/dNAnIaVHPZnee7kXpZL55M9htd30r7Q==} hasBin: true - '@ecies/ciphers@0.2.1': - resolution: {integrity: sha512-ezMihhjW24VNK/2qQR7lH8xCQY24nk0XHF/kwJ1OuiiY5iEwQXOcKVSy47fSoHPRG8gVGXcK5SgtONDk5xMwtQ==} + '@ecies/ciphers@0.2.3': + resolution: {integrity: sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} peerDependencies: '@noble/ciphers': ^1.0.0 - '@emnapi/core@1.3.1': - resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==} + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} - '@emnapi/runtime@1.3.1': - resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} - '@emnapi/wasi-threads@1.0.1': - resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==} + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -1377,366 +1272,170 @@ packages: '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.0': - resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.0': - resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.0': - resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.0': - resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.0': - resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.0': - resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.0': - resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.0': - resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.0': - resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.0': - resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.0': - resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.0': - resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.0': - resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.0': - resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.0': - resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.0': - resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.0': - resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-arm64@0.25.0': - resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': - resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-arm64@0.25.0': - resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': - resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.0': - resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.0': - resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.0': - resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.0': - resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.20.0': - resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.2.1': - resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@fastify/ajv-compiler@4.0.2': + resolution: {integrity: sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==} - '@eslint/core@0.14.0': - resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@fastify/error@4.1.0': + resolution: {integrity: sha512-KeFcciOr1eo/YvIXHP65S94jfEEqn1RxTRBT1aJaHxY5FK0/GDXYozsQMMWlZoHgi8i0s+YtrLsgj/JkUUjSkQ==} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@eslint/js@9.27.0': - resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.3.1': - resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@fastify/ajv-compiler@4.0.1': - resolution: {integrity: sha512-DxrBdgsjNLP0YM6W5Hd6/Fmj43S8zMKiFJYgi+Ri3htTGAowPVG/tG1wpnWLMjufEnehRivUCKZ1pLDIoZdTuw==} - - '@fastify/error@4.0.0': - resolution: {integrity: sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==} - - '@fastify/fast-json-stringify-compiler@5.0.1': - resolution: {integrity: sha512-f2d3JExJgFE3UbdFcpPwqNUEoHWmt8pAKf8f+9YuLESdefA0WgqxeT6DrGL4Yrf/9ihXNSKOqpjEmurV405meA==} + '@fastify/fast-json-stringify-compiler@5.0.3': + resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} '@fastify/forwarded@3.0.0': resolution: {integrity: sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==} - '@fastify/merge-json-schemas@0.1.1': - resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + '@fastify/merge-json-schemas@0.2.1': + resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} '@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb': resolution: {tarball: https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb} @@ -1750,11 +1449,11 @@ packages: '@fastify/sensible@6.0.3': resolution: {integrity: sha512-Iyn8698hp/e5+v8SNBBruTa7UfrMEP52R16dc9jMpqSyEcPsvWFQo+R6WwHCUnJiLIsuci2ZoEZ7ilrSSCPIVg==} - '@floating-ui/core@1.6.9': - resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} + '@floating-ui/core@1.7.0': + resolution: {integrity: sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==} - '@floating-ui/dom@1.6.13': - resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} + '@floating-ui/dom@1.7.0': + resolution: {integrity: sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==} '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} @@ -1765,35 +1464,6 @@ packages: react: ^16.8.5 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@humanwhocodes/retry@0.4.2': - resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} - engines: {node: '>=18.18'} - '@hutson/parse-repository-url@3.0.2': resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} engines: {node: '>=6.9.0'} @@ -1804,81 +1474,20 @@ packages: prop-types: ^15.0.0 react: '>=0.14.0' - '@inquirer/figures@1.0.7': - resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} + '@inquirer/figures@1.0.11': + resolution: {integrity: sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==} engines: {node: '>=18'} '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': @@ -1934,22 +1543,19 @@ packages: peerDependencies: '@mikro-orm/core': ^6.0.0 - '@napi-rs/wasm-runtime@0.2.4': - resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} - - '@napi-rs/wasm-runtime@0.2.7': - resolution: {integrity: sha512-5yximcFK5FNompXfJFoWanu5l8v1hNGqNHh9du1xETp9HWk/B/PzvchX55WYOPaIeNglG8++68AAiauBAtbnzw==} + '@napi-rs/wasm-runtime@0.2.9': + resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} - '@noble/ciphers@1.0.0': - resolution: {integrity: sha512-wH5EHOmLi0rEazphPbecAzmjd12I6/Yv/SiHdkA9LSycsQk7RuuTp7am5/o62qYr0RScE7Pc9icXGBbsr6cesA==} + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.6.0': - resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==} + '@noble/curves@1.9.0': + resolution: {integrity: sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.5.0': - resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': @@ -1964,127 +1570,37 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nx/devkit@21.0.3': - resolution: {integrity: sha512-PnEZWenJ3fOoAU+Es9v0xxANyrROtFj+rjDHCjfyqGs3jMihMyTsCDQLpsjdnrUF5jjp9VUawfms76ocSLmwpw==} - peerDependencies: - nx: 21.0.3 + '@opentelemetry/api-logs@0.57.2': + resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} - '@nx/eslint@21.0.3': - resolution: {integrity: sha512-0YNNO5iTPIq8j4vTluVTIXM1Be3GOvB1n930oupZYVvnQIR0Zv7SO9fnoz+boyZfeFhjBcy74xeiymz8eoAsDA==} + '@opentelemetry/context-async-hooks@1.30.1': + resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} + engines: {node: '>=14'} peerDependencies: - '@zkochan/js-yaml': 0.0.7 - eslint: ^8.0.0 || ^9.0.0 - peerDependenciesMeta: - '@zkochan/js-yaml': - optional: true + '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@nx/jest@21.0.3': - resolution: {integrity: sha512-a0BXZT4MScDXtxfaQuKpggMpMhItjsZIww4N0k4PpuDh0Yxuf643sZzIVCAkIBP6BoC2gFk00eF79U+6S2x+zg==} + '@opentelemetry/core@1.30.1': + resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@nx/js@21.0.3': - resolution: {integrity: sha512-nrlMpSd567zGbZDyj4BTUcZAKzjTqzvx6B2+zmkq+q8RqApGOs3xvJ6QJpFrcaC7Oqa9xZljDUbaDE7bPueAMA==} + '@opentelemetry/instrumentation-amqplib@0.46.1': + resolution: {integrity: sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==} + engines: {node: '>=14'} peerDependencies: - verdaccio: ^6.0.5 - peerDependenciesMeta: - verdaccio: - optional: true + '@opentelemetry/api': ^1.3.0 - '@nx/node@21.0.3': - resolution: {integrity: sha512-Tb8pGq+VgAEnc6Lf3q+hCF68m/w+8IqzREk3/PVSHQBprCgFkE5/LYMH6Bx+yt3w+LULY2/q5EMkh8v4vjt7Iw==} - - '@nx/nx-darwin-arm64@21.0.3': - resolution: {integrity: sha512-UQxDwhLcA1ERv4u1GiNgb2yhTHflWE8iOfayApPfYD0eSjBUMj30/s2E1RVq5Tx9TxYtmFVwz+C8DxOVWKu3OQ==} - cpu: [arm64] - os: [darwin] - - '@nx/nx-darwin-x64@21.0.3': - resolution: {integrity: sha512-ZR9a2ysE4nrQ2VTQxZa2w76rr9rA9kw61Oy7sp2rlKeqr8yyKynZgZmuCTnOOn3LCOUl072wtGCIS85SFSeGug==} - cpu: [x64] - os: [darwin] - - '@nx/nx-freebsd-x64@21.0.3': - resolution: {integrity: sha512-bJRFvhTOzewDM2HxeVDqbrR5357tAUpovcj9czzRGrEhhoelqCLP0/9Ric1V4j8yyPXmRpXa9REWq3weFaAcwg==} - cpu: [x64] - os: [freebsd] - - '@nx/nx-linux-arm-gnueabihf@21.0.3': - resolution: {integrity: sha512-7Mt/G0e3x9j83VuM1wflbAGTITO+VZBRKZpvhWS6Z6mNzNhc6T2PX2OvNMDC7PsUlTJeq7O4kb3M1fmkmk1DVA==} - cpu: [arm] - os: [linux] - - '@nx/nx-linux-arm64-gnu@21.0.3': - resolution: {integrity: sha512-3sUnzts/dquniJ+IXrJJcxnwl4jqbteJJhSXtrYlp+Kd2nNqgQIqdKvHy2hwUBDD0NvzpDdz6bTwcY2s1ghsAg==} - cpu: [arm64] - os: [linux] - - '@nx/nx-linux-arm64-musl@21.0.3': - resolution: {integrity: sha512-gBr2QXy5zmyut/UHbQLKV+wq6IKJ+5AACsczH4JdUvr58e0GunIVWTArgHMZwDJxbY4hAxtwgB8rFD4Bi6noxQ==} - cpu: [arm64] - os: [linux] - - '@nx/nx-linux-x64-gnu@21.0.3': - resolution: {integrity: sha512-hwm/ER8LC1Dkh1CNIx9D3GqYFdX99StyDMV1A+W9fnIehJmFq8Om0HrbLrJAFIFMvQpVxwMjDO39q6Kf/UWyhg==} - cpu: [x64] - os: [linux] - - '@nx/nx-linux-x64-musl@21.0.3': - resolution: {integrity: sha512-Rg0xjGoikWbhnEANSP3KwRtYHJmq1P1pv31zvPjeZI9nFNLyCRsJYSpnlE5BfP8a8XlzdqlLO0Df0XmL5Fdyew==} - cpu: [x64] - os: [linux] - - '@nx/nx-win32-arm64-msvc@21.0.3': - resolution: {integrity: sha512-LyxCffeta+4ad70043ZQ1/lFdOzpFpx8zmwVLhASTmZ6jdrePKPyxn+uSv0AWOiEVpGiZHr3Yh47YfrlWBO+wA==} - cpu: [arm64] - os: [win32] - - '@nx/nx-win32-x64-msvc@21.0.3': - resolution: {integrity: sha512-1lyRNwjDax8Nvemt8wpbYiyRjIvrnBrzZTEkm7z5rDV2RX2Ik06EOZHWWtqHmdfx1EPV2omvVWRmmqLHI98YLA==} - cpu: [x64] - os: [win32] - - '@nx/vite@21.0.3': - resolution: {integrity: sha512-UrjFI+ikI32756UTx7CyJvD0f/Mxin++d8wElrZqlR6bwaevRDoQX0bkB4y9eQkUk9Va3XX1zwXEKq2CYE7QxQ==} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vitest: ^1.3.1 || ^2.0.0 || ^3.0.0 - - '@nx/web@21.0.3': - resolution: {integrity: sha512-PrQcibXZJGmCjwsKOqAoSAkaHk+jT511S8s/RNwwOFGPQigpRoCmLFIxGF9N3T478qufuGS21jU6UbgWDgf9xg==} - - '@nx/workspace@21.0.3': - resolution: {integrity: sha512-yM1hCR7kbN0VuXum2P6m5SY+CXqSAez5fJYh8vHtXRfnzGRoerQJS2G2ZYQ828sxLeXB4Tft50IUUAgHEVh8tw==} - - '@opentelemetry/api-logs@0.57.2': - resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} - engines: {node: '>=14'} - - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/context-async-hooks@1.30.1': - resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/core@1.30.1': - resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/instrumentation-amqplib@0.46.1': - resolution: {integrity: sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-connect@0.43.1': - resolution: {integrity: sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-connect@0.43.1': + resolution: {integrity: sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 '@opentelemetry/instrumentation-dataloader@0.16.1': resolution: {integrity: sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==} @@ -2232,8 +1748,8 @@ packages: resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} engines: {node: '>=14'} - '@opentelemetry/semantic-conventions@1.30.0': - resolution: {integrity: sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==} + '@opentelemetry/semantic-conventions@1.33.0': + resolution: {integrity: sha512-TIpZvE8fiEILFfTlfPnltpBaD3d9/+uQHVCyC3vfdh6WfCXKhNFzoP5RyDDIndfvZC5GrA4pyEDNyjPloJud+w==} engines: {node: '>=14'} '@opentelemetry/sql-common@0.40.1': @@ -2242,76 +1758,77 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 - '@oxc-resolver/binding-darwin-arm64@5.0.0': - resolution: {integrity: sha512-zwHAf+owoxSWTDD4dFuwW+FkpaDzbaL30H5Ltocb+RmLyg4WKuteusRLKh5Y8b/cyu7UzhxM0haIqQjyqA1iuA==} + '@oxc-resolver/binding-darwin-arm64@5.3.0': + resolution: {integrity: sha512-hXem5ZAguS7IlSiHg/LK0tEfLj4eUo+9U6DaFwwBEGd0L0VIF9LmuiHydRyOrdnnmi9iAAFMAn/wl2cUoiuruA==} cpu: [arm64] os: [darwin] - '@oxc-resolver/binding-darwin-x64@5.0.0': - resolution: {integrity: sha512-1lS3aBNVjVQKBvZdHm13+8tSjvu2Tl1Cv4FnUyMYxqx6+rsom2YaOylS5LhDUwfZu0zAgpLMwK6kGpF/UPncNg==} + '@oxc-resolver/binding-darwin-x64@5.3.0': + resolution: {integrity: sha512-wgSwfsZkRbuYCIBLxeg1bYrtKnirAy+IJF0lwfz4z08clgdNBDbfGECJe/cd0csIZPpRcvPFe8317yf31sWhtA==} cpu: [x64] os: [darwin] - '@oxc-resolver/binding-freebsd-x64@5.0.0': - resolution: {integrity: sha512-q9sRd68wC1/AJ0eu6ClhxlklVfe8gH4wrUkSyEbIYTZ8zY5yjsLY3fpqqsaCvWJUx65nW+XtnAxCGCi5AXr1Mw==} + '@oxc-resolver/binding-freebsd-x64@5.3.0': + resolution: {integrity: sha512-kzeE2WHgcRMmWjB071RdwEV5Pwke4o0WWslCKoh8if1puvxIxfzu3o7g6P2+v77BP5qop4cri+uvLABSO0WZjg==} cpu: [x64] os: [freebsd] - '@oxc-resolver/binding-linux-arm-gnueabihf@5.0.0': - resolution: {integrity: sha512-catYavWsvqViYnCveQjhrK6yVYDEPFvIOgGLxnz5r2dcgrjpmquzREoyss0L2QG/J5HTTbwqwZ1kk+g56hE/1A==} + '@oxc-resolver/binding-linux-arm-gnueabihf@5.3.0': + resolution: {integrity: sha512-I8np34yZP/XfIkZNDbw3rweqVgfjmHYpNX3xnJZWg+f4mgO9/UNWBwetSaqXeDZqvIch/aHak+q4HVrQhQKCqg==} cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm64-gnu@5.0.0': - resolution: {integrity: sha512-l/0pWoQM5kVmJLg4frQ1mKZOXgi0ex/hzvFt8E4WK2ifXr5JgKFUokxsb/oat7f5YzdJJh5r9p+qS/t3dA26Aw==} + '@oxc-resolver/binding-linux-arm64-gnu@5.3.0': + resolution: {integrity: sha512-u2ndfeEUrW898eXM+qPxIN8TvTPjI90NDQBRgaxxkOfNw3xaotloeiZGz5+Yzlfxgvxr9DY9FdYkqhUhSnGhOw==} cpu: [arm64] os: [linux] - '@oxc-resolver/binding-linux-arm64-musl@5.0.0': - resolution: {integrity: sha512-bx0oz/oaAW4FGYqpIIxJCnmgb906YfMhTEWCJvYkxjpEI8VKLJEL3PQevYiqDq36SA0yRLJ/sQK2fqry8AFBfA==} + '@oxc-resolver/binding-linux-arm64-musl@5.3.0': + resolution: {integrity: sha512-TzbjmFkcnESGuVItQ2diKacX8vu5G0bH3BHmIlmY4OSRLyoAlrJFwGKAHmh6C9+Amfcjo2rx8vdm7swzmsGC6Q==} cpu: [arm64] os: [linux] - '@oxc-resolver/binding-linux-x64-gnu@5.0.0': - resolution: {integrity: sha512-4PH++qbSIhlRsFYdN1P9neDov4OGhTGo5nbQ1D7AL6gWFLo3gdZTc00FM2y8JjeTcPWEXkViZuwpuc0w5i6qHg==} + '@oxc-resolver/binding-linux-riscv64-gnu@5.3.0': + resolution: {integrity: sha512-NH3pjAqh8nuN29iRuRfTY42Vn03ctoR9VE8llfoUKUfhHUjFHYOXK5VSkhjj1usG8AeuesvqrQnLptCRQVTi/Q==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@5.3.0': + resolution: {integrity: sha512-tuZtkK9sJYh2MC2uhol1M/8IMTB6ZQ5jmqP2+k5XNXnOb/im94Y5uV/u2lXwVyIuKHZZHtr+0d1HrOiNahoKpw==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@5.3.0': + resolution: {integrity: sha512-VzhPYmZCtoES/ThcPdGSmMop7JlwgqtSvlgtKCW15ByV2JKyl8kHAHnPSBfpIooXb0ehFnRdxFtL9qtAEWy01g==} cpu: [x64] os: [linux] - '@oxc-resolver/binding-linux-x64-musl@5.0.0': - resolution: {integrity: sha512-mLfQFpX3/5y9oWi0b+9FbWDkL2hM0Y29653beCHiHxAdGyVgb2DsJbK74WkMTwtSz9by8vyBh8jGPZcg1yLZbQ==} + '@oxc-resolver/binding-linux-x64-musl@5.3.0': + resolution: {integrity: sha512-Hi39cWzul24rGljN4Vf1lxjXzQdCrdxO5oCT7KJP4ndSlqIUODJnfnMAP1YhcnIRvNvk+5E6sZtnEmFUd/4d8Q==} cpu: [x64] os: [linux] - '@oxc-resolver/binding-wasm32-wasi@5.0.0': - resolution: {integrity: sha512-uEhsAZSo65qsRi6+IfBTEUUFbjg7T2yruJeLYpFfEATpm3ory5Mgo5vx3L0c2/Cz1OUZXBgp3A8x6VMUB2jT2A==} + '@oxc-resolver/binding-wasm32-wasi@5.3.0': + resolution: {integrity: sha512-ddujvHhP3chmHnSXRlkPVUeYj4/B7eLZwL4yUid+df3WCbVh6DgoT9RmllZn21AhxgKtMdekDdyVJYKFd8tl4A==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-resolver/binding-win32-arm64-msvc@5.0.0': - resolution: {integrity: sha512-8DbSso9Jp1ns8AYuZFXdRfAcdJrzZwkFm/RjPuvAPTENsm685dosBF8G6gTHQlHvULnk6o3sa9ygZaTGC/UoEw==} + '@oxc-resolver/binding-win32-arm64-msvc@5.3.0': + resolution: {integrity: sha512-j1YYPLvUkMVNKmIFQZZJ7q6Do4cI3htUnyxNLwDSBVhSohvPIK2VG+IdtOAlWZGa7v+phEZsHfNbXVwB0oPYFQ==} cpu: [arm64] os: [win32] - '@oxc-resolver/binding-win32-x64-msvc@5.0.0': - resolution: {integrity: sha512-ylppfPEg63NuRXOPNsXFlgyl37JrtRn0QMO26X3K3Ytp5HtLrMreQMGVtgr30e1l2YmAWqhvmKlCryOqzGPD/g==} + '@oxc-resolver/binding-win32-x64-msvc@5.3.0': + resolution: {integrity: sha512-LT9eOPPUqfZscQRd5mc08RBeDWOQf+dnOrKnanMallTGPe6g7+rcAlFTA8SWoJbcD45PV8yArFtCmSQSpzHZmg==} cpu: [x64] os: [win32] - '@phenomnomnominal/tsquery@5.0.1': - resolution: {integrity: sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==} - peerDependencies: - typescript: ^3 || ^4 || ^5 - '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.0': - resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - - '@polka/url@1.0.0-next.28': - resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -2329,8 +1846,8 @@ packages: '@remirror/core-constants@2.0.2': resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} - '@remix-run/router@1.21.0': - resolution: {integrity: sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==} + '@remix-run/router@1.23.0': + resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} '@rollup/plugin-babel@6.0.4': @@ -2400,183 +1917,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.24.0': - resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.40.0': - resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.24.0': - resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-android-arm64@4.40.0': - resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==} + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.24.0': - resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-arm64@4.40.0': - resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==} + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.24.0': - resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.40.0': - resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==} + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.40.0': - resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==} + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.40.0': - resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==} + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.24.0': - resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.40.0': - resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.24.0': - resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.40.0': - resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.24.0': - resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.40.0': - resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.24.0': - resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.40.0': - resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.40.0': - resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': - resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': - resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.24.0': - resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.40.0': - resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.40.0': - resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.24.0': - resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.40.0': - resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.24.0': - resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.40.0': - resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.24.0': - resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.40.0': - resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-win32-arm64-msvc@4.24.0': - resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-arm64-msvc@4.40.0': - resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.24.0': - resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.40.0': - resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==} + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.24.0': - resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.40.0': - resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==} + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} cpu: [x64] os: [win32] @@ -2660,15 +2097,6 @@ packages: engines: {node: '>=18'} hasBin: true - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@styled-system/background@5.1.2': resolution: {integrity: sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==} @@ -2724,70 +2152,69 @@ packages: '@swc-node/sourcemap-support@0.5.1': resolution: {integrity: sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg==} - '@swc/core-darwin-arm64@1.11.22': - resolution: {integrity: sha512-upSiFQfo1TE2QM3+KpBcp5SrOdKKjoc+oUoD1mmBDU2Wv4Bjjv16Z2I5ADvIqMV+b87AhYW+4Qu6iVrQD7j96Q==} + '@swc/core-darwin-arm64@1.11.24': + resolution: {integrity: sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.11.22': - resolution: {integrity: sha512-8PEuF/gxIMJVK21DjuCOtzdqstn2DqnxVhpAYfXEtm3WmMqLIOIZBypF/xafAozyaHws4aB/5xmz8/7rPsjavw==} + '@swc/core-darwin-x64@1.11.24': + resolution: {integrity: sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.11.22': - resolution: {integrity: sha512-NIPTXvqtn9e7oQHgdaxM9Z/anHoXC3Fg4ZAgw5rSGa1OlnKKupt5sdfJamNggSi+eAtyoFcyfkgqHnfe2u63HA==} + '@swc/core-linux-arm-gnueabihf@1.11.24': + resolution: {integrity: sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.11.22': - resolution: {integrity: sha512-xZ+bgS60c5r8kAeYsLNjJJhhQNkXdidQ277pUabSlu5GjR0CkQUPQ+L9hFeHf8DITEqpPBPRiAiiJsWq5eqMBg==} + '@swc/core-linux-arm64-gnu@1.11.24': + resolution: {integrity: sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.11.22': - resolution: {integrity: sha512-JhrP/q5VqQl2eJR0xKYIkKTPjgf8CRsAmRnjJA2PtZhfQ543YbYvUqxyXSRyBOxdyX8JwzuAxIPEAlKlT7PPuQ==} + '@swc/core-linux-arm64-musl@1.11.24': + resolution: {integrity: sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.11.22': - resolution: {integrity: sha512-htmAVL+U01gk9GyziVUP0UWYaUQBgrsiP7Ytf6uDffrySyn/FclUS3MDPocNydqYsOpj3OpNKPxkaHK+F+X5fg==} + '@swc/core-linux-x64-gnu@1.11.24': + resolution: {integrity: sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.11.22': - resolution: {integrity: sha512-PL0VHbduWPX+ANoyOzr58jBiL2VnD0xGSFwPy7NRZ1Pr6SNWm4jw3x2u6RjLArGhS5EcWp64BSk9ZxqmTV3FEg==} + '@swc/core-linux-x64-musl@1.11.24': + resolution: {integrity: sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.11.22': - resolution: {integrity: sha512-moJvFhhTVGoMeEThtdF7hQog80Q00CS06v5uB+32VRuv+I31+4WPRyGlTWHO+oY4rReNcXut/mlDHPH7p0LdFg==} + '@swc/core-win32-arm64-msvc@1.11.24': + resolution: {integrity: sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.11.22': - resolution: {integrity: sha512-/jnsPJJz89F1aKHIb5ScHkwyzBciz2AjEq2m9tDvQdIdVufdJ4SpEDEN9FqsRNRLcBHjtbLs6bnboA+B+pRFXw==} + '@swc/core-win32-ia32-msvc@1.11.24': + resolution: {integrity: sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.11.22': - resolution: {integrity: sha512-lc93Y8Mku7LCFGqIxJ91coXZp2HeoDcFZSHCL90Wttg5xhk5xVM9uUCP+OdQsSsEixLF34h5DbT9ObzP8rAdRw==} + '@swc/core-win32-x64-msvc@1.11.24': + resolution: {integrity: sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.11.22': - resolution: {integrity: sha512-mjPYbqq8XjwqSE0hEPT9CzaJDyxql97LgK4iyvYlwVSQhdN1uK0DBG4eP9PxYzCS2MUGAXB34WFLegdUj5HGpg==} + '@swc/core@1.11.24': + resolution: {integrity: sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==} engines: {node: '>=10'} - deprecated: It has a bug. See https://github.com/swc-project/swc/issues/10413 peerDependencies: '@swc/helpers': '>=0.5.17' peerDependenciesMeta: @@ -2814,13 +2241,13 @@ packages: peerDependencies: '@tiptap/pm': ^2.0.0 - '@tiptap/extension-blockquote@2.8.0': - resolution: {integrity: sha512-m3CKrOIvV7fY1Ak2gYf5LkKiz6AHxHpg6wxfVaJvdBqXgLyVtHo552N+A4oSHOSRbB4AG9EBQ2NeBM8cdEQ4MA==} + '@tiptap/extension-blockquote@2.12.0': + resolution: {integrity: sha512-XUC2A77YAPMJS2SqZ2S62IGcUH8gZ7cdhoWlYQb1pR4ZzXFByeKDJPxfYeAePSiuI01YGrlzgY2c6Ncx/DtO0A==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-bold@2.8.0': - resolution: {integrity: sha512-U1YkZBxDkSLNvPNiqxB5g42IeJHr27C7zDb/yGQN2xL4UBeg4O9xVhCFfe32f6tLwivSL0dar4ScElpaCJuqow==} + '@tiptap/extension-bold@2.12.0': + resolution: {integrity: sha512-lAUtoLDLRc5ofD2I9MFY6MQ7d1qBLLqS1rvpwaPjOaoQb/GPVnaHj9qXYG0SY9K3erMtto48bMFpAcscjZHzZQ==} peerDependencies: '@tiptap/core': ^2.7.0 @@ -2830,12 +2257,10 @@ packages: '@tiptap/core': ^2.0.0 '@tiptap/pm': ^2.0.0 - '@tiptap/extension-bullet-list@2.8.0': - resolution: {integrity: sha512-H4O2X0ozbc/ce9/XF1H98sqWVUdtt7jzy7hMBunwmY8ZxI4dHtcRkeg81CZbpKTqOqRrMCLWjE3M2tgiDXrDkA==} + '@tiptap/extension-bullet-list@2.12.0': + resolution: {integrity: sha512-YTCjztB8MaIpwyxFYr81H4+LdKCq1VlaSXQyrPdB44mVdhhRqc46BYQb8/B//XE3UIu3X2QWFjwrqRlUq6vUiw==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-list-item': ^2.7.0 - '@tiptap/extension-text-style': ^2.7.0 '@tiptap/extension-character-count@2.1.13': resolution: {integrity: sha512-FxPxS/Uqd4MgndInxXOcgNd225541Nsk1lT5e2uNTSNiQnG7dj7cSFG5KXGcSGLpGGt6e/E28WR6KLV+0/u+WA==} @@ -2843,8 +2268,8 @@ packages: '@tiptap/core': ^2.0.0 '@tiptap/pm': ^2.0.0 - '@tiptap/extension-code-block@2.8.0': - resolution: {integrity: sha512-POuA5Igx+Dto0DTazoBFAQTj/M/FCdkqRVD9Uhsxhv49swPyANTJRr05vgbgtHB+NDDsZfCawVh7pI0IAD/O0w==} + '@tiptap/extension-code-block@2.12.0': + resolution: {integrity: sha512-1D7cYAjgxEFHdfC/35Ooi4GqWKB5sszbW8iI7N16XILNln26xb0d5KflXqYrwr9CN/ZnZoCl2o6YsP7xEObcZA==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 @@ -2859,8 +2284,8 @@ packages: peerDependencies: '@tiptap/core': ^2.0.0 - '@tiptap/extension-dropcursor@2.8.0': - resolution: {integrity: sha512-rAFvx44YuT6dtS1c+ALw0ROAGI16l5L1HxquL4hR1gtxDcTieST5xhw5bkshXlmrlfotZXPrhokzqA7qjhZtJw==} + '@tiptap/extension-dropcursor@2.12.0': + resolution: {integrity: sha512-zcZSOXFj+7LVnmdPWTfKr5AoxYIzFPFlLJe35AdTQC5IhkljLn1Exct8I30ZREojX/00hKYsO7JJmePS6TEVlQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 @@ -2871,14 +2296,14 @@ packages: '@tiptap/core': ^2.0.0 '@tiptap/pm': ^2.0.0 - '@tiptap/extension-gapcursor@2.8.0': - resolution: {integrity: sha512-Be1LWCmvteQInOnNVN+HTqc1XWsj1bCl+Q7et8qqNjtGtTaCbdCp8ppcH1SKJxNTM/RLUtPyJ8FDgOTj51ixCA==} + '@tiptap/extension-gapcursor@2.12.0': + resolution: {integrity: sha512-k8ji5v9YKn7bNjo8UtI9hEfXfl4tKUp1hpJOEmUxGJQa3LIwrwSbReupUTnHszGQelzxikS/l1xO9P0TIGwRoA==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-hard-break@2.8.0': - resolution: {integrity: sha512-vqiIfviNiCmy/pJTHuDSCAGL2O4QDEdDmAvGJu8oRmElUrnlg8DbJUfKvn6DWQHNSQwRb+LDrwWlzAYj1K9u6A==} + '@tiptap/extension-hard-break@2.12.0': + resolution: {integrity: sha512-08MNS2PK5DzdnAfqXn4krmJ/xebKmWpRpYqqN5EM8AvetYKlAJyTVSpo0ZUeGbZ3EZiPm9djgSnrLqpFUDjRCg==} peerDependencies: '@tiptap/core': ^2.7.0 @@ -2887,14 +2312,14 @@ packages: peerDependencies: '@tiptap/core': ^2.0.0 - '@tiptap/extension-history@2.8.0': - resolution: {integrity: sha512-u5YS0J5Egsxt8TUWMMAC3QhPZaak+IzQeyHch4gtqxftx96tprItY7AD/A3pGDF2uCSnN+SZrk6yVexm6EncDw==} + '@tiptap/extension-history@2.12.0': + resolution: {integrity: sha512-+B9CAf2BFURC6mQiM1OQtahVTzdEOEgT/UUNlRZkeeBc0K5of3dr6UdBqaoaMAefja3jx5PqiQ7mhUBAjSt6AA==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-horizontal-rule@2.8.0': - resolution: {integrity: sha512-Sn/MI8WVFBoIYSIHA9NJryJIyCEzZdRysau8pC5TFnfifre0QV1ksPz2bgF+DyCD69ozQiRdBBHDEwKe47ZbfQ==} + '@tiptap/extension-horizontal-rule@2.12.0': + resolution: {integrity: sha512-Vi2+6RIehDSpoJn/7PDuOieUj7W7WrEb4wBxK9TG8PDscihR0mehhhzm/K2xhH4TN48iPJGRsjDFrFjTbXmcnw==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 @@ -2904,8 +2329,8 @@ packages: peerDependencies: '@tiptap/core': ^2.0.0 - '@tiptap/extension-italic@2.8.0': - resolution: {integrity: sha512-PwwSE2LTYiHI47NJnsfhBmPiLE8IXZYqaSoNPU6flPrk1KxEzqvRI1joKZBmD9wuqzmHJ93VFIeZcC+kfwi8ZA==} + '@tiptap/extension-italic@2.12.0': + resolution: {integrity: sha512-JKcXK3LmEsmxNzEq5e06rPUGMRLUxmJ2mYtBY4NlJ6yLM9XMDljtgeTnWT0ySLYmfINSFTkX4S7WIRbpl9l4pw==} peerDependencies: '@tiptap/core': ^2.7.0 @@ -2915,25 +2340,23 @@ packages: '@tiptap/core': ^2.0.0 '@tiptap/pm': ^2.0.0 - '@tiptap/extension-list-item@2.8.0': - resolution: {integrity: sha512-o7OGymGxB0B9x3x2prp3KBDYFuBYGc5sW69O672jk8G52DqhzzndgPnkk0qUn8nXAUKuDGbJmpmHVA2kagqnRg==} + '@tiptap/extension-list-item@2.12.0': + resolution: {integrity: sha512-4YwZooC8HP+gPxs6YrkB1ayggyYbgVvJx/rWBT6lKSW2MVVg8QXi1zAcSI3MhIhHmqDysXXFPL8JURlbeGjaFA==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-ordered-list@2.8.0': - resolution: {integrity: sha512-sCvNbcTS1+5QTTXwUPFa10vf5I1pr8sGcOTIh0G+a5ZkS5+6FxT12k7VLzPt39QyNbOi+77U2o4Xr4XyaEkfSg==} + '@tiptap/extension-ordered-list@2.12.0': + resolution: {integrity: sha512-1ys0e/oqk09oXxrB1WzAx5EntK/QreObG/V1yhgihGm429fxHMsxzIYN6dKAYxx0YOPQG7qEZRrrPuWU70Ms7g==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-list-item': ^2.7.0 - '@tiptap/extension-text-style': ^2.7.0 - '@tiptap/extension-paragraph@2.8.0': - resolution: {integrity: sha512-XgxxNNbuBF48rAGwv7/s6as92/xjm/lTZIGTq9aG13ClUKFtgdel7C33SpUCcxg3cO2WkEyllXVyKUiauFZw/A==} + '@tiptap/extension-paragraph@2.12.0': + resolution: {integrity: sha512-QNK5cgewCunWFxpLlbvvoO1rrLgEtNKxiY79fctP9toV+e59R+1i1Q9lXC1O5mOfDgVxCb6uFDMsqmKhFjpPog==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-strike@2.8.0': - resolution: {integrity: sha512-ezkDiXxQ3ME/dDMMM7tAMkKRi6UWw7tIu+Mx7Os0z8HCGpVBk1gFhLlhEd8I5rJaPZr4tK1wtSehMA9bscFGQw==} + '@tiptap/extension-strike@2.12.0': + resolution: {integrity: sha512-nBaa5YtBsLJPZFfSs36sBz4Zgi/c8b3MsmS/Az8uXaHb0R9yPewOVUMDIQbxMct8SXUlIo9VtKlOL+mVJ3Nkpw==} peerDependencies: '@tiptap/core': ^2.7.0 @@ -2963,11 +2386,6 @@ packages: peerDependencies: '@tiptap/core': ^2.0.0 - '@tiptap/extension-text-style@2.8.0': - resolution: {integrity: sha512-jJp0vcZ2Ty7RvIL0VU6dm1y+fTfXq1lN2GwtYzYM0ueFuESa+Qo8ticYOImyWZ3wGJGVrjn7OV9r0ReW0/NYkQ==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/extension-text@2.1.13': resolution: {integrity: sha512-zzsTTvu5U67a8WjImi6DrmpX2Q/onLSaj+LRWPh36A1Pz2WaxW5asZgaS+xWCnR+UrozlCALWa01r7uv69jq0w==} peerDependencies: @@ -2992,8 +2410,8 @@ packages: '@tiptap/starter-kit@2.1.13': resolution: {integrity: sha512-ph/mUR/OwPtPkZ5rNHINxubpABn8fHnvJSdhXFrY/q6SKoaO11NZXgegRaiG4aL7O6Sz4LsZVw6Sm0Ae+GJmrg==} - '@ts-morph/common@0.26.0': - resolution: {integrity: sha512-/RmKAtctStXqM5nECMQ46duT74Hoig/DBzhWXGHcodlDNrgRbsbwwHqSKFNbca6z9Xt/CUWMeXOsC9QEN1+rqw==} + '@ts-morph/common@0.26.1': + resolution: {integrity: sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA==} '@tsconfig/node-lts@22.0.1': resolution: {integrity: sha512-BwlbLiYurZKrj+Pa6etSE1jXmr3VEDdgJto1jEYKcpBVwZZSWVkCPyFEFYbHdIOaFMlSTtV206DYPlT109aqug==} @@ -3019,14 +2437,14 @@ packages: '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} '@types/bcrypt@5.0.2': resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==} @@ -3034,11 +2452,14 @@ packages: '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/config@3.3.5': + resolution: {integrity: sha512-itq2HtXQBrNUKwMNZnb9mBRE3T99VYCdl1gjST9rq+9kFaB1iMMGuDeZnP88qid73DnpAMKH9ZolqDpS1Lz7+w==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/conventional-commits-parser@5.0.0': - resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + '@types/conventional-commits-parser@5.0.1': + resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} '@types/cross-spawn@6.0.6': resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} @@ -3049,30 +2470,12 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/hoist-non-react-statics@3.3.6': resolution: {integrity: sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -3114,8 +2517,8 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@19.0.4': - resolution: {integrity: sha512-3O4QisJDYr1uTUMZHA2YswiQZRq+Pd8D+GdVFYikTutYsTz+QZgWkAPnP7rx9txoI6EXKcPiluMqWPFV3tT9Wg==} + '@types/react@19.1.4': + resolution: {integrity: sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -3132,9 +2535,6 @@ packages: '@types/sinonjs__fake-timers@8.1.5': resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/tedious@4.0.14': resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} @@ -3144,93 +2544,6 @@ packages: '@types/validator@13.12.2': resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - - '@typescript-eslint/eslint-plugin@8.32.1': - resolution: {integrity: sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/parser@6.21.0': - resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@8.32.1': - resolution: {integrity: sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/scope-manager@6.21.0': - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/scope-manager@8.32.1': - resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@8.32.1': - resolution: {integrity: sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/types@6.21.0': - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/types@8.32.1': - resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@6.21.0': - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/typescript-estree@8.32.1': - resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/utils@8.32.1': - resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/visitor-keys@6.21.0': - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} - engines: {node: ^16.0.0 || >=18.0.0} - - '@typescript-eslint/visitor-keys@8.32.1': - resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@vitest/coverage-v8@3.1.3': resolution: {integrity: sha512-cj76U5gXCl3g88KSnf80kof6+6w+K4BjOflCl7t6yRJPDuCrHtVu0SgNYOUARJOL5TI8RScDbm5x4s1/P9bvpw==} peerDependencies: @@ -3274,17 +2587,6 @@ packages: '@vitest/utils@3.1.3': resolution: {integrity: sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==} - '@yarnpkg/lockfile@1.1.0': - resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} - - '@yarnpkg/parsers@3.0.2': - resolution: {integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==} - engines: {node: '>=18.12.0'} - - '@zkochan/js-yaml@0.0.7': - resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} - hasBin: true - JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -3297,22 +2599,12 @@ packages: peerDependencies: acorn: ^8 - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.4: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} engines: {node: '>=0.4.0'} hasBin: true @@ -3340,16 +2632,9 @@ packages: ajv: optional: true - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -3358,10 +2643,6 @@ packages: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} - ansi-regex@2.1.1: - resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} - engines: {node: '>=0.10.0'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3370,10 +2651,6 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} - ansi-styles@2.2.1: - resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} - engines: {node: '>=0.10.0'} - ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -3382,10 +2659,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -3393,21 +2666,14 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} array-ify@1.0.0: @@ -3421,19 +2687,13 @@ packages: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} - assert-never@1.3.0: - resolution: {integrity: sha512-9Z3vxQ+berkL/JJo0dK+EY3Lp0s3NtSnP3VCLsh5HDcZPrh0M+KQRK5sWhUeyPPH+/RCxZqOxLMR+YC6vlviEQ==} + assert-never@1.4.0: + resolution: {integrity: sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==} assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - async@2.6.4: - resolution: {integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -3445,50 +2705,28 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - avvio@9.0.0: - resolution: {integrity: sha512-UbYrOXgE/I+knFG+3kJr9AgC7uNo8DG+FGGODpH9Bj1O1kL/QDjBXnTem9leD3VdQKtaHjV3O85DQ7hHh4IIHw==} - - axios@1.7.9: - resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} - - axios@1.8.3: - resolution: {integrity: sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==} + avvio@9.1.0: + resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-const-enum@1.2.0: - resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} - babel-plugin-polyfill-corejs2@0.4.12: - resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==} + babel-plugin-polyfill-corejs2@0.4.13: + resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-corejs3@0.10.6: - resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} + babel-plugin-polyfill-corejs3@0.11.1: + resolution: {integrity: sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.3: - resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==} + babel-plugin-polyfill-regenerator@0.6.4: + resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -3497,36 +2735,12 @@ packages: peerDependencies: styled-components: '>= 2' - babel-plugin-transform-typescript-metadata@0.3.2: - resolution: {integrity: sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==} - peerDependencies: - '@babel/core': ^7 - '@babel/traverse': ^7 - peerDependenciesMeta: - '@babel/traverse': - optional: true - - babel-preset-current-node-syntax@1.1.0: - resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} - peerDependencies: - '@babel/core': ^7.0.0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - basic-auth@2.0.1: - resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} - engines: {node: '>= 0.8'} - bcrypt@6.0.0: resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} engines: {node: '>= 18'} @@ -3547,14 +2761,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + browserslist@4.24.5: + resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -3572,8 +2783,8 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -3603,17 +2814,13 @@ packages: camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} - caniuse-lite@1.0.30001692: - resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} + caniuse-lite@1.0.30001718: + resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} - chalk@1.1.3: - resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} - engines: {node: '>=0.10.0'} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3626,10 +2833,6 @@ packages: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -3637,12 +2840,8 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.4.1: - resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -3659,10 +2858,6 @@ packages: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} - cli-spinners@2.6.1: - resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} - engines: {node: '>=6'} - cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} @@ -3690,16 +2885,9 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - code-block-writer@13.0.3: resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -3719,10 +2907,6 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - columnify@1.6.0: - resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} - engines: {node: '>=8.0.0'} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -3746,10 +2930,6 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - common-tags@1.8.2: - resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} - engines: {node: '>=4.0.0'} - commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -3763,6 +2943,10 @@ packages: resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} engines: {'0': node >= 6.0} + config@4.0.0: + resolution: {integrity: sha512-zOzfDfcpaBUMRvTH+pbj111Gfb+1BMvCy9EcE5ckuugfAcks3FIQMyt8TRA30dhuGnZNBxfbPBUKtuM03ih/nA==} + engines: {node: '>= 20.0.0'} + conventional-changelog-angular@5.0.13: resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} engines: {node: '>=10'} @@ -3852,20 +3036,16 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} - core-js-compat@3.40.0: - resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==} + core-js-compat@3.42.0: + resolution: {integrity: sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - corser@2.0.1: - resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} - engines: {node: '>= 0.4.0'} - cosmiconfig-typescript-loader@6.1.0: resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==} engines: {node: '>=v18'} @@ -3905,14 +3085,10 @@ packages: cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} - cross-spawn@6.0.5: - resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} engines: {node: '>=4.8'} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3954,23 +3130,6 @@ packages: dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.3.1: - resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -4006,14 +3165,6 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -4022,9 +3173,6 @@ packages: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -4036,10 +3184,6 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -4060,8 +3204,8 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} detect-newline@3.1.0: @@ -4073,10 +3217,6 @@ packages: engines: {node: '>= 4.0.0'} hasBin: true - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -4085,13 +3225,6 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -4099,14 +3232,6 @@ packages: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} - dotenv-expand@11.0.7: - resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} - engines: {node: '>=12'} - - dotenv@16.4.7: - resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} - engines: {node: '>=12'} - dotenv@16.5.0: resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} @@ -4125,21 +3250,12 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - eciesjs@0.4.11: - resolution: {integrity: sha512-SmUG449n1w1YGvJD9R30tBGvpxTxA0cnn0rfvpFIBvmezfIhagLjsH2JG8HBHOLS8slXsPh48II7IDUTH/J3Mg==} + eciesjs@0.4.14: + resolution: {integrity: sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - electron-to-chromium@1.5.80: - resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} - - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} + electron-to-chromium@1.5.152: + resolution: {integrity: sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -4153,10 +3269,6 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - enquirer@2.3.6: - resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} - engines: {node: '>=8.6'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -4172,10 +3284,6 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -4194,13 +3302,12 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} - engines: {node: '>=18'} - hasBin: true + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} - esbuild@0.25.0: - resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} engines: {node: '>=18'} hasBin: true @@ -4212,95 +3319,19 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-prettier@10.1.5: - resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-prettier@5.4.0: - resolution: {integrity: sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-scope@8.3.0: - resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - - eslint@9.27.0: - resolution: {integrity: sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -4311,9 +3342,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -4321,18 +3349,10 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} @@ -4346,21 +3366,12 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-json-stringify@6.0.0: - resolution: {integrity: sha512-FGMKZwniMTgZh7zQp9b6XnBVxUmKVahQLQeRQHqwYmPDqDhcEKZ3BaQsxelFFI5PY7nN71OEeiL47/zUWcYe1A==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-json-stringify@6.0.1: + resolution: {integrity: sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==} fast-querystring@1.1.2: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} @@ -4372,9 +3383,6 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@2.4.0: - resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} - fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} @@ -4387,9 +3395,6 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fdir@6.4.4: resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} peerDependencies: @@ -4405,17 +3410,6 @@ packages: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -4424,9 +3418,9 @@ packages: resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} engines: {node: '>=6'} - find-my-way@9.1.0: - resolution: {integrity: sha512-Y5jIsuYR4BwWDYYQ2A/RWWE6gD8a0FMgtU+HOq1WKku+Cwdz8M1v8wcAmRXXM1/iqtoqg06v+LjAxMYbCjViMw==} - engines: {node: '>=14'} + find-my-way@9.3.0: + resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} + engines: {node: '>=20'} find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} @@ -4451,14 +3445,6 @@ packages: resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} engines: {node: '>=18'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true @@ -4475,15 +3461,16 @@ packages: debug: optional: true - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.1: - resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} forwarded-parse@2.1.2: @@ -4493,12 +3480,6 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - front-matter@4.0.2: - resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -4528,14 +3509,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.2.0: - resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -4557,11 +3534,8 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-them-args@1.3.2: - resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==} - - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} getopts@2.3.0: resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} @@ -4592,23 +3566,15 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@11.0.0: - resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} + glob@11.0.2: + resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} engines: {node: 20 || >=22} hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -4622,14 +3588,6 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - globals@16.1.0: resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==} engines: {node: '>=18'} @@ -4638,9 +3596,6 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4648,9 +3603,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - gud@1.0.0: resolution: {integrity: sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==} @@ -4663,15 +3615,9 @@ packages: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} - harmony-reflect@1.6.2: - resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} - - has-ansi@2.0.0: - resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} - engines: {node: '>=0.10.0'} - - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} @@ -4684,14 +3630,6 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -4704,10 +3642,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -4721,14 +3655,6 @@ packages: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} - hosted-git-info@7.0.2: - resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} - engines: {node: ^16.14.0 || >=18.0.0} - - html-encoding-sniffer@3.0.0: - resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} - engines: {node: '>=12'} - html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -4739,15 +3665,6 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - http-proxy@1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} - engines: {node: '>=8.0.0'} - - http-server@14.1.1: - resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} - engines: {node: '>=12'} - hasBin: true - https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -4764,8 +3681,8 @@ packages: i18next-browser-languagedetector@7.2.2: resolution: {integrity: sha512-6b7r75uIJDWCcCflmbof+sJ94k9UQO4X0YR62oUfqGI/GjCLVzlCwu8TFdRZIqVLzWbzNcmkmhfqKEr4TLz4HQ==} - i18next-http-backend@2.7.1: - resolution: {integrity: sha512-vPksHIckysGgykCD8JwCr2YsJEml9Cyw+Yu2wtb4fQ7xIn9RH/hkUDh5UkwnIzb0kSL4SJ30Ab/sCInhQxbCgg==} + i18next-http-backend@2.7.3: + resolution: {integrity: sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==} i18next@22.5.1: resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} @@ -4774,14 +3691,6 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - identity-obj-proxy@3.0.0: - resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} - engines: {node: '>=4'} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -4789,15 +3698,11 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.4: - resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} - engines: {node: '>= 4'} - immutable@4.3.7: resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} import-in-the-middle@1.13.2: @@ -4806,10 +3711,6 @@ packages: import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -4836,8 +3737,8 @@ packages: resolution: {integrity: sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==} engines: {node: '>=18'} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} interpret@2.2.0: @@ -4847,49 +3748,41 @@ packages: interrogator@2.0.1: resolution: {integrity: sha512-HPilaDW0ZSPEKhhj6NcklQi7jhYyad1r8l6tS9hYCxvVnlrrJAUMZ7GuGa5PFK3RmquLSk+iml2geBJjC+Yc9g==} - ip-regex@4.3.0: - resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} - engines: {node: '>=8'} - ipaddr.js@2.2.0: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} - is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} engines: {node: '>= 0.4'} - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -4906,10 +3799,6 @@ packages: resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} engines: {node: '>=18'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -4929,8 +3818,8 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} is-number@7.0.0: @@ -4941,10 +3830,6 @@ packages: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-plain-obj@1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -4959,28 +3844,28 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} is-text-path@1.0.1: @@ -4999,25 +3884,14 @@ packages: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} - is-url@1.2.4: - resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} - is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} - is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - - is2@2.0.9: - resolution: {integrity: sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==} - engines: {node: '>=v0.10.0'} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -5039,22 +3913,10 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.6: resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} @@ -5066,116 +3928,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.0.2: - resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==} + jackspeak@4.1.0: + resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} engines: {node: 20 || >=22} - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} - engines: {node: '>=10'} - hasBin: true - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jiti@2.4.2: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true @@ -5187,10 +3943,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -5205,27 +3957,18 @@ packages: engines: {node: '>=6'} hasBin: true - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-ref-resolver@1.0.1: - resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-ref-resolver@2.0.1: + resolution: {integrity: sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==} json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} @@ -5234,9 +3977,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -5247,13 +3987,6 @@ packages: jw-paginate@1.0.4: resolution: {integrity: sha512-W0bv782exgCoynUL/egbRpaYwf/r6T6e02H870H5u3hfSgEYrxgz5POwmFF5aApS6iPi6yhZ0VF8IbafNFsntA==} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kill-port@1.6.1: - resolution: {integrity: sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==} - hasBin: true - kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -5286,16 +4019,8 @@ packages: tedious: optional: true - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - light-my-request@6.1.0: - resolution: {integrity: sha512-+NFuhlOGoEwxeQfJ/pobkVFxcnKyDtiX847hLjuB/IzBxIl3q4VJeFI8uRCgb3AlTWL1lgOr+u5+8QdUcr33ng==} + light-my-request@6.6.0: + resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} @@ -5304,15 +4029,11 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lines-and-columns@2.0.3: - resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - linkifyjs@4.2.0: - resolution: {integrity: sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw==} + linkifyjs@4.3.1: + resolution: {integrity: sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==} lint-staged@16.0.0: resolution: {integrity: sha512-sUCprePs6/rbx4vKC60Hez6X10HPkpDJaGcy3D1NdwR7g1RcNkWL8q9mJMreOqmHBTs+1sNFp+wOiX9fr+hoOQ==} @@ -5404,13 +4125,6 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - loglevel-colored-level-prefix@1.0.0: - resolution: {integrity: sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==} - - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -5421,8 +4135,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.0.1: - resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} + lru-cache@11.1.0: + resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -5449,9 +4163,6 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -5509,11 +4220,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -5537,10 +4243,6 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -5556,10 +4258,6 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -5572,8 +4270,8 @@ packages: resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} engines: {node: '>=0.10.0'} - module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} moment-timezone@0.5.46: resolution: {integrity: sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==} @@ -5581,8 +4279,8 @@ packages: moment@2.30.1: resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} ms@2.1.2: @@ -5602,14 +4300,11 @@ packages: resolution: {integrity: sha512-BfcvzBlUTxSDWfT+oH7vd6CbUV+rThLLHCIym/QO6GGLBsyVXleZs00fto2i2jzC/wPiBYk5jyOmpXWg4YopiA==} engines: {node: '>=20.18'} - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -5637,12 +4332,6 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - node-machine-id@1.1.12: - resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -5653,39 +4342,14 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-package-arg@11.0.1: - resolution: {integrity: sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==} - engines: {node: ^16.14.0 || >=18.0.0} - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - nx@21.0.3: - resolution: {integrity: sha512-MWKucgA00TRjMBsuGbAS6HrCnOVwktU7Zxxw06Rfl0ue9tfTqbZX5iiNnb6M7b2wPQm9zcQXEq3DVBkPP8wUNw==} - engines: {node: ^20.19.0 || ^22.12.0} - hasBin: true - peerDependencies: - '@swc-node/register': ^1.8.0 - '@swc/core': ^1.3.85 - peerDependenciesMeta: - '@swc-node/register': - optional: true - '@swc/core': - optional: true - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -5702,8 +4366,8 @@ packages: resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} engines: {node: '>= 10'} - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} obuf@1.1.2: @@ -5724,22 +4388,6 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - - opener@1.5.2: - resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} - hasBin: true - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@5.3.0: - resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} - engines: {node: '>=10'} - ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} @@ -5755,8 +4403,8 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} - oxc-resolver@5.0.0: - resolution: {integrity: sha512-66fopyAqCN8Mx4tzNiBXWbk8asCSuxUWN62gwTc3yfRs7JfWhX/eVJCf+fUrfbNOdQVOWn+o8pAKllp76ysMXA==} + oxc-resolver@5.3.0: + resolution: {integrity: sha512-FHqtZx0idP5QRPSNcI5g2ItmADg7fhR3XIeWg5eRMGfp44xqRpfkdvo+EX4ZceqV9bxvl0Z8vaqMqY0gYaNYNA==} p-limit@1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} @@ -5835,10 +4483,6 @@ packages: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@2.0.1: resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} engines: {node: '>=4'} @@ -5882,14 +4526,11 @@ packages: pg-connection-string@2.7.0: resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} - pg-connection-string@2.8.5: - resolution: {integrity: sha512-Ni8FuZ8yAF+sWZzojvtLE2b03cqjO5jNULcHFfM9ZZ0/JXrgom5pBREbtnAw7oxsxJqHw9Nz/XWORUEL3/IFow==} - pg-connection-string@2.9.0: resolution: {integrity: sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==} - pg-cursor@2.12.0: - resolution: {integrity: sha512-rppw54OnuYZfMUjiJI2zJMwAjjt2V9EtLUb+t7V5tqwSE5Jxod+7vA7Y0FI6Nq976jNLciA0hoVkwvjjB8qzEw==} + pg-cursor@2.15.0: + resolution: {integrity: sha512-sO3SQP9seXoaV7sddeKrPQk3zhnrMchCr71cYjkyNHCC6n3mA5AAyhK5foxy+yBtnBZuPqxzoiVxz3jKyV7D3g==} peerDependencies: pg: ^8 @@ -5910,11 +4551,6 @@ packages: peerDependencies: pg: '>=8.0' - pg-pool@3.9.6: - resolution: {integrity: sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==} - peerDependencies: - pg: '>=8.0' - pg-protocol@1.10.0: resolution: {integrity: sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==} @@ -5994,8 +4630,8 @@ packages: resolution: {integrity: sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==} hasBin: true - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} pkg-dir@3.0.0: @@ -6010,12 +4646,8 @@ packages: resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} engines: {node: '>=10'} - portfinder@1.0.32: - resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==} - engines: {node: '>= 0.12.0'} - - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} postcss-value-parser@4.2.0: @@ -6064,39 +4696,6 @@ packages: postgres-range@1.1.4: resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier-eslint@16.4.2: - resolution: {integrity: sha512-vtJAQEkaN8fW5QKl08t7A5KCjlZuDUNeIlr9hgolMS5s3+uzbfRHDwaRnzrdqnY2YpHDmeDS/8zY0MKQHXJtaA==} - engines: {node: '>=16.10.0'} - peerDependencies: - prettier-plugin-svelte: ^3.0.0 - svelte-eslint-parser: '*' - peerDependenciesMeta: - prettier-plugin-svelte: - optional: true - svelte-eslint-parser: - optional: true - - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} - engines: {node: '>=14'} - hasBin: true - - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -6116,17 +4715,17 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - prosemirror-changeset@2.2.1: - resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==} + prosemirror-changeset@2.3.0: + resolution: {integrity: sha512-8wRKhlEwEJ4I13Ju54q2NZR1pVKGTgJ/8XsQ8L5A5uUsQ/YQScQJuEAuh8Bn8i6IwAMjjLRABd9lVli+DlIiVw==} prosemirror-collab@1.3.1: resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} - prosemirror-commands@1.6.1: - resolution: {integrity: sha512-tNy4uaGWzvuUYXDke7B28krndIrdQJhSh0OLpubtwtEwFbjItOj/eoAfPvstBJyyV0S2+b5t4G+4XPXdxar6pg==} + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} - prosemirror-dropcursor@1.8.1: - resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==} + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} prosemirror-gapcursor@1.3.2: resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==} @@ -6134,32 +4733,32 @@ packages: prosemirror-history@1.4.1: resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} - prosemirror-inputrules@1.4.0: - resolution: {integrity: sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==} + prosemirror-inputrules@1.5.0: + resolution: {integrity: sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==} - prosemirror-keymap@1.2.2: - resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==} + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} - prosemirror-markdown@1.13.1: - resolution: {integrity: sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==} + prosemirror-markdown@1.13.2: + resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==} - prosemirror-menu@1.2.4: - resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==} + prosemirror-menu@1.2.5: + resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==} - prosemirror-model@1.23.0: - resolution: {integrity: sha512-Q/fgsgl/dlOAW9ILu4OOhYWQbc7TQd4BwKH/RwmUjyVf8682Be4zj3rOYdLnYEcGzyg8LL9Q5IWYKD8tdToreQ==} + prosemirror-model@1.25.1: + resolution: {integrity: sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==} - prosemirror-schema-basic@1.2.3: - resolution: {integrity: sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==} + prosemirror-schema-basic@1.2.4: + resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} - prosemirror-schema-list@1.4.1: - resolution: {integrity: sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==} + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} prosemirror-state@1.4.3: resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} - prosemirror-tables@1.5.0: - resolution: {integrity: sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==} + prosemirror-tables@1.7.1: + resolution: {integrity: sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==} prosemirror-trailing-node@2.0.9: resolution: {integrity: sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg==} @@ -6168,11 +4767,11 @@ packages: prosemirror-state: ^1.4.2 prosemirror-view: ^1.33.8 - prosemirror-transform@1.10.2: - resolution: {integrity: sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==} + prosemirror-transform@1.10.4: + resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==} - prosemirror-view@1.34.3: - resolution: {integrity: sha512-mKZ54PrX19sSaQye+sef+YjBbNu2voNwLS1ivb6aD2IRmxRGW64HU9B644+7OfJStGLyxvOreKqEgfvXa91WIA==} + prosemirror-view@1.39.2: + resolution: {integrity: sha512-BmOkml0QWNob165gyUxXi5K5CVUgVPpqMEAAml/qzgKn9boLUWVPzQ6LtzXw8Cn1GtRQX4ELumPxqtLTDaAKtg==} proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -6188,9 +4787,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} @@ -6199,10 +4795,6 @@ packages: (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) - qs@6.13.1: - resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} - engines: {node: '>=0.6'} - qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -6220,10 +4812,10 @@ packages: raf-schd@4.0.3: resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} - react-currency-input-field@3.9.0: - resolution: {integrity: sha512-OmkO0rRSGiNGbcO4F1wzC+Szm2A7tLRGtDAKF6t0xNrFr07q99AHo3BAn/68RTEG4iwqc2m2jekKZi33/8SV+Q==} + react-currency-input-field@3.10.0: + resolution: {integrity: sha512-GRmZogHh1e1LrmgXg/fKHSuRLYUnj/c/AumfvfuDMA0UX1mDR6u2NR0fzDemRdq4tNHNLucJeJ2OKCr3ehqyDA==} peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-datepicker@4.25.0: resolution: {integrity: sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==} @@ -6263,8 +4855,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-onclickoutside@6.13.1: - resolution: {integrity: sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w==} + react-onclickoutside@6.13.2: + resolution: {integrity: sha512-h6Hbf1c8b7tIYY4u90mDdBLY4+AGQVMFtIE89HgC0DtVCh/JfKl477gYqUtGLmjZBKK3MJxomP/lFiLbz4sq9A==} peerDependencies: react: ^15.5.x || ^16.x || ^17.x || ^18.x react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x @@ -6303,21 +4895,21 @@ packages: redux: optional: true - react-router-dom@6.28.1: - resolution: {integrity: sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==} + react-router-dom@6.30.0: + resolution: {integrity: sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' - react-router@6.28.1: - resolution: {integrity: sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==} + react-router@6.30.0: + resolution: {integrity: sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' - react-select@5.9.0: - resolution: {integrity: sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==} + react-select@5.10.1: + resolution: {integrity: sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -6388,11 +4980,8 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regenerator-transform@0.15.2: - resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - - regexp.prototype.flags@1.5.3: - resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} regexpu-core@6.2.0: @@ -6414,16 +5003,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - require-in-the-middle@7.4.0: - resolution: {integrity: sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ==} + require-in-the-middle@7.5.2: + resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} engines: {node: '>=8.6.0'} - require-relative@0.8.7: - resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6435,12 +5018,9 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} hasBin: true restore-cursor@3.1.0: @@ -6469,18 +5049,13 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rimraf@6.0.1: resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} engines: {node: 20 || >=22} hasBin: true - rollup-plugin-esbuild-minify@1.2.0: - resolution: {integrity: sha512-M112JoRC8oUHKHQcXUQzSwXEnvriXtpy4rnaeqt/ZeknVbjymhdZRRUf+2fdR3k4jOQCZas0N4u1U2xbtRTrbg==} + rollup-plugin-esbuild-minify@1.3.0: + resolution: {integrity: sha512-y7BDyMMGYhq5901EijNABWgjEzC8myYhOXKmlnU8xIRvX7KQucSWABBR3IEyITuLJFyq/rXIlezDh9zvnR0k2w==} engines: {node: '>= 14.18'} peerDependencies: rollup: ^2 || ^3 || ^4 @@ -6490,13 +5065,8 @@ packages: peerDependencies: rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 - rollup@4.24.0: - resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rollup@4.40.0: - resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==} + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6510,8 +5080,8 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -6519,8 +5089,12 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex2@4.0.0: - resolution: {integrity: sha512-Hvjfv25jPDVr3U+4LDzBuZPPOymELG3PYcSk5hcevooo1yxxamQL/bHs/GrEPGmMoMEwRrHVGiCA1pXi97B8Ew==} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-regex2@5.0.0: + resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} @@ -6532,9 +5106,6 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - secure-compare@3.0.1: - resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} - secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -6554,8 +5125,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true @@ -6596,8 +5167,8 @@ packages: tedious: optional: true - set-cookie-parser@2.7.0: - resolution: {integrity: sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==} + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} @@ -6633,9 +5204,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-exec@1.0.2: - resolution: {integrity: sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==} - shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -6655,10 +5223,6 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - side-channel@1.1.0: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} @@ -6700,12 +5264,6 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - - source-map-support@0.5.19: - resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} - source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -6726,8 +5284,8 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.20: - resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} split2@3.2.2: resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} @@ -6739,17 +5297,10 @@ packages: split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sqlstring@2.3.3: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -6769,18 +5320,14 @@ packages: resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -6803,10 +5350,6 @@ packages: resolution: {integrity: sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==} deprecated: This module is not used anymore, and has been replaced by @npmcli/package-json - strip-ansi@3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -6819,10 +5362,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -6854,10 +5393,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - supports-color@2.0.0: - resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} - engines: {node: '>=0.8.0'} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -6866,33 +5401,14 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.11.1: - resolution: {integrity: sha512-fWZqNBZNNFp/7mTUy1fSsydhKsAKJ+u90Nk7kOK5Gcq9vObaqLBLjWFDBkyVU9Vvc6Y71VbOevMuGhqv02bT+Q==} - engines: {node: ^14.18.0 || >=16.0.0} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - tarn@3.0.2: resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} engines: {node: '>=8.0.0'} - tcp-port-used@1.0.2: - resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - test-exclude@7.0.1: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} @@ -6908,9 +5424,6 @@ packages: text-mask-addons@3.8.0: resolution: {integrity: sha512-VSZSdc/tKn4zGxgpJ+uNBzoW1t472AoAFIlbw1K7hSNXz0DfSBYDJNRxLqgxOfWw1BY2z6DQpm7g0sYZn5qLpg==} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -6950,8 +5463,8 @@ packages: resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} engines: {node: '>=12.0.0'} - tinymce@7.9.0: - resolution: {integrity: sha512-tTrUmUGWqy1BY1WwDYh4WiuHm23LiRTcE1Xq3WLO8HKFzde/d0bTF/hXWOa97zqGh0ndJHx/nysQaNC9Gcd16g==} + tinymce@6.8.5: + resolution: {integrity: sha512-qAL/FxL7cwZHj4BfaF818zeJJizK9jU5IQzTcSLL4Rj5MaJdiVblEj7aDr80VCV1w9h4Lak9hlnALhq/kVtN1g==} tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} @@ -6972,13 +5485,6 @@ packages: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} - tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} - engines: {node: '>=14.14'} - - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -7001,26 +5507,10 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -7041,10 +5531,6 @@ packages: '@swc/wasm': optional: true - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -7056,22 +5542,10 @@ packages: type-assertions@1.1.0: resolution: {integrity: sha512-LJ5h6n63vxS8fSdfTPqIc6IrbCo9X3g6Se+wSikCGsqaAI3ajN0iputclNG07wdWfBoQZIrpASjBQo5BeVNrAg==} - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - type-fest@0.18.1: resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} engines: {node: '>=10'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -7091,18 +5565,6 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint-language-service@5.0.5: - resolution: {integrity: sha512-b7gWXpwSTqMVKpPX3WttNZEyVAMKs/2jsHKF79H+qaD6mjzCyU5jboJe/lOZgLJD+QRsXCr0GjIVxvl5kI1NMw==} - peerDependencies: - '@typescript-eslint/parser': '>= 5.0.0' - eslint: '>= 8.0.0' - typescript: '>= 4.0.0' - - typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -7142,16 +5604,12 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - union@0.5.0: - resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} - engines: {node: '>= 0.8.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - update-browserslist-db@1.1.2: - resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -7160,12 +5618,6 @@ packages: resolution: {integrity: sha512-zTWmRiOJACCdFGWjzye3L5cjSuVdZ/c8C0iHIwVbfORFD8IhGNAO6BOWkZ+fj+SI6/aFbdjGXE6gwPG780H4gQ==} engines: {node: '>=4'} - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - url-join@4.0.1: - resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} - use-isomorphic-layout-effect@1.2.0: resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} peerDependencies: @@ -7180,8 +5632,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - use-sync-external-store@1.4.0: - resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -7199,17 +5651,9 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - validator@13.12.0: resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} engines: {node: '>= 0.10'} @@ -7295,18 +5739,9 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} - vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} @@ -7316,22 +5751,19 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - whatwg-encoding@2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} - engines: {node: '>=12'} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} which-collection@1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} which@1.3.1: @@ -7356,10 +5788,6 @@ packages: wkx@0.5.0: resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -7382,10 +5810,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - xss@1.0.15: resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} engines: {node: '>= 0.10.0'} @@ -7409,11 +5833,6 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} - hasBin: true - yaml@2.7.1: resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} engines: {node: '>= 14'} @@ -7443,8 +5862,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} engines: {node: '>=12.20'} yoctocolors-cjs@2.1.2: @@ -7453,7 +5872,7 @@ packages: snapshots: - '@adminjs/design-system@4.1.1(@babel/core@7.26.0)(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@types/react@19.0.4)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)': + '@adminjs/design-system@4.1.1(@babel/core@7.27.1)(@types/react@19.1.4)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)': dependencies: '@hypnosphi/create-react-context': 0.3.1(prop-types@15.8.1)(react@18.3.1) '@tinymce/tinymce-react': 4.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -7475,7 +5894,7 @@ snapshots: '@tiptap/extension-typography': 2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) '@tiptap/pm': 2.1.13 '@tiptap/react': 2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tiptap/starter-kit': 2.1.13(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@tiptap/pm@2.1.13) + '@tiptap/starter-kit': 2.1.13(@tiptap/pm@2.1.13) date-fns: 2.30.0 flat: 5.0.2 hoist-non-react-statics: 3.3.2 @@ -7483,19 +5902,18 @@ snapshots: lodash: 4.17.21 polished: 4.3.1 react: 18.3.1 - react-currency-input-field: 3.9.0(react@18.3.1) + react-currency-input-field: 3.10.0(react@18.3.1) react-datepicker: 4.25.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-feather: 2.0.10(react@18.3.1) react-phone-input-2: 2.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-select: 5.9.0(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.10.1(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-text-mask: 5.5.0(react@18.3.1) - styled-components: 5.3.9(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1) + styled-components: 5.3.9(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1) styled-system: 5.1.5 text-mask-addons: 3.8.0 transitivePeerDependencies: - '@babel/core' - - '@tiptap/extension-text-style' - '@types/react' - prop-types - react-is @@ -7503,34 +5921,29 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.25.7': + '@babel/code-frame@7.27.1': dependencies: - '@babel/highlight': 7.25.7 - picocolors: 1.1.1 - - '@babel/code-frame@7.26.2': - dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.5': {} + '@babel/compat-data@7.27.2': {} - '@babel/core@7.26.0': + '@babel/core@7.27.1': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.5 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.5 - '@babel/template': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) - '@babel/types': 7.26.5 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) + '@babel/helpers': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.1(supports-color@5.5.0) + '@babel/types': 7.27.1 convert-source-map: 2.0.0 debug: 4.4.1(supports-color@5.5.0) gensync: 1.0.0-beta.2 @@ -7539,805 +5952,708 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.26.5': + '@babel/generator@7.27.1': dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 - '@jridgewell/gen-mapping': 0.3.5 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.25.9': + '@babel/helper-annotate-as-pure@7.27.1': dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.27.1 - '@babel/helper-compilation-targets@7.26.5': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.26.5 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 + '@babel/compat-data': 7.27.2 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.24.5 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.1) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.0)': + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 regexpu-core: 6.2.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': + '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.1(supports-color@5.5.0) lodash.debounce: 4.0.8 - resolve: 1.22.8 + resolve: 1.22.10 transitivePeerDependencies: - supports-color - '@babel/helper-member-expression-to-functions@7.25.9': + '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.26.5(supports-color@5.5.0) - '@babel/types': 7.26.5 + '@babel/traverse': 7.27.1(supports-color@5.5.0) + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.25.9(supports-color@5.5.0)': + '@babel/helper-module-imports@7.27.1(supports-color@5.5.0)': dependencies: - '@babel/traverse': 7.26.5(supports-color@5.5.0) - '@babel/types': 7.26.5 + '@babel/traverse': 7.27.1(supports-color@5.5.0) + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.25.9': + '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.27.1 - '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-wrap-function': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-wrap-function': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.0)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.26.5(supports-color@5.5.0) - '@babel/types': 7.26.5 + '@babel/traverse': 7.27.1(supports-color@5.5.0) + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.25.9': {} - - '@babel/helper-validator-identifier@7.25.7': {} + '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-wrap-function@7.25.9': + '@babel/helper-wrap-function@7.27.1': dependencies: - '@babel/template': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) - '@babel/types': 7.26.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.1(supports-color@5.5.0) + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helpers@7.26.0': + '@babel/helpers@7.27.1': dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.5 + '@babel/template': 7.27.2 + '@babel/types': 7.27.1 - '@babel/highlight@7.25.7': + '@babel/parser@7.27.2': dependencies: - '@babel/helper-validator-identifier': 7.25.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/parser@7.26.5': - dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.1) + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.26.5(@babel/core@7.26.0)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-block-scoping@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-classes@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.1) + '@babel/traverse': 7.27.1(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/template': 7.25.9 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 - '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-destructuring@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.0)': + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/core': 7.27.1 + '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-nullish-coalescing-operator@7.26.5(@babel/core@7.26.0)': + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-object-rest-spread@7.27.2(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-react-display-name@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/types': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.1) + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-regenerator@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - regenerator-transform: 0.15.2 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-runtime@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-runtime@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/helper-plugin-utils': 7.26.5 - babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) - babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) - babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.1) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.1) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.1) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.26.5(@babel/core@7.26.0)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - - '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/preset-env@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/compat-data': 7.26.5 - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0) - '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0) - '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoped-functions': 7.26.5(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-exponentiation-operator': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.26.5(@babel/core@7.26.0) - '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0) - babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) - babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) - babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) - core-js-compat: 3.40.0 + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.1) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.27.2(@babel/core@7.27.1)': + dependencies: + '@babel/compat-data': 7.27.2 + '@babel/core': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.1) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.27.1) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-block-scoping': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-object-rest-spread': 7.27.2(@babel/core@7.27.1) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-regenerator': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.27.1) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.27.1) + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.1) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.1) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.1) + core-js-compat: 3.42.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/types': 7.26.5 + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.27.1 esutils: 2.0.3 - '@babel/preset-react@7.26.3(@babel/core@7.26.0)': + '@babel/preset-react@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-react-pure-annotations': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)': + '@babel/preset-typescript@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-typescript': 7.26.5(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - '@babel/register@7.25.9(@babel/core@7.26.0)': + '@babel/register@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.27.1 clone-deep: 4.0.1 find-cache-dir: 2.1.0 make-dir: 2.1.0 - pirates: 4.0.6 + pirates: 4.0.7 source-map-support: 0.5.21 - '@babel/runtime@7.26.0': - dependencies: - regenerator-runtime: 0.14.1 + '@babel/runtime@7.27.1': {} - '@babel/template@7.25.9': + '@babel/template@7.27.2': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 - '@babel/traverse@7.26.5(supports-color@5.5.0)': + '@babel/traverse@7.27.1(supports-color@5.5.0)': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.5 - '@babel/parser': 7.26.5 - '@babel/template': 7.25.9 - '@babel/types': 7.26.5 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/template': 7.27.2 + '@babel/types': 7.27.1 debug: 4.4.1(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.26.5': + '@babel/types@7.27.1': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - - '@bcoe/v8-coverage@0.2.3': {} + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 '@bcoe/v8-coverage@1.0.2': {} @@ -8418,7 +6734,7 @@ snapshots: '@commitlint/is-ignored@19.8.1': dependencies: '@commitlint/types': 19.8.1 - semver: 7.7.1 + semver: 7.7.2 '@commitlint/lint@19.8.1': dependencies: @@ -8483,7 +6799,7 @@ snapshots: '@commitlint/types@19.8.1': dependencies: - '@types/conventional-commits-parser': 5.0.0 + '@types/conventional-commits-parser': 5.0.1 chalk: 5.4.1 '@cspotcode/source-map-support@0.8.1': @@ -8505,13 +6821,13 @@ snapshots: '@databases/migrations-base@3.0.1': dependencies: - assert-never: 1.3.0 + assert-never: 1.4.0 chalk: 4.1.2 deep-equal: 2.2.3 interrogator: 2.0.1 is-interactive: 1.0.0 parameter-reducers: 2.1.0 - semver: 7.7.1 + semver: 7.7.2 '@databases/pg-config@3.2.0(typescript@5.8.3)': dependencies: @@ -8531,12 +6847,12 @@ snapshots: '@databases/migrations-base': 3.0.1 '@databases/pg': 5.5.0(typescript@5.8.3) '@databases/pg-config': 3.2.0(typescript@5.8.3) - assert-never: 1.3.0 + assert-never: 1.4.0 chalk: 4.1.2 interrogator: 2.0.1 is-interactive: 1.0.0 parameter-reducers: 2.1.0 - semver: 7.6.3 + semver: 7.7.2 sucrase: 3.35.0 transitivePeerDependencies: - pg-native @@ -8568,7 +6884,7 @@ snapshots: '@databases/pg-data-type-id': 3.0.0 '@databases/pg-schema-introspect': 4.2.0(typescript@5.8.3) '@databases/shared-print-types': 1.2.0 - assert-never: 1.3.0 + assert-never: 1.4.0 transitivePeerDependencies: - typescript @@ -8577,7 +6893,7 @@ snapshots: '@databases/pg-config': 3.2.0(typescript@5.8.3) '@databases/with-container': 2.1.1 '@types/cross-spawn': 6.0.6 - cross-spawn: 6.0.5 + cross-spawn: 6.0.6 modern-spawn: 1.0.0 ms: 2.1.3 parameter-reducers: 2.1.0 @@ -8588,7 +6904,7 @@ snapshots: '@databases/pg@5.5.0(typescript@5.8.3)': dependencies: - '@babel/code-frame': 7.25.7 + '@babel/code-frame': 7.27.1 '@databases/escape-identifier': 1.0.3 '@databases/pg-config': 3.2.0(typescript@5.8.3) '@databases/pg-connection-string': 1.0.0 @@ -8598,9 +6914,9 @@ snapshots: '@databases/shared': 3.1.0 '@databases/split-sql-query': 1.0.4(@databases/sql@3.3.0) '@databases/sql': 3.3.0 - assert-never: 1.3.0 + assert-never: 1.4.0 pg: 8.16.0 - pg-cursor: 2.12.0(pg@8.16.0) + pg-cursor: 2.15.0(pg@8.16.0) transitivePeerDependencies: - pg-native - typescript @@ -8637,7 +6953,7 @@ snapshots: '@databases/with-container@2.1.1': dependencies: '@types/cross-spawn': 6.0.6 - cross-spawn: 6.0.5 + cross-spawn: 6.0.6 detect-port: 1.6.1 modern-spawn: 1.0.0 transitivePeerDependencies: @@ -8647,7 +6963,7 @@ snapshots: dependencies: commander: 11.1.0 dotenv: 16.5.0 - eciesjs: 0.4.11 + eciesjs: 0.4.14 execa: 5.1.1 fdir: 6.4.4(picomatch@4.0.2) ignore: 5.3.2 @@ -8655,27 +6971,30 @@ snapshots: picomatch: 4.0.2 which: 4.0.0 - '@ecies/ciphers@0.2.1(@noble/ciphers@1.0.0)': + '@ecies/ciphers@0.2.3(@noble/ciphers@1.3.0)': dependencies: - '@noble/ciphers': 1.0.0 + '@noble/ciphers': 1.3.0 - '@emnapi/core@1.3.1': + '@emnapi/core@1.4.3': dependencies: - '@emnapi/wasi-threads': 1.0.1 + '@emnapi/wasi-threads': 1.0.2 tslib: 2.8.1 + optional: true - '@emnapi/runtime@1.3.1': + '@emnapi/runtime@1.4.3': dependencies: tslib: 2.8.1 + optional: true - '@emnapi/wasi-threads@1.0.1': + '@emnapi/wasi-threads@1.0.2': dependencies: tslib: 2.8.1 + optional: true '@emotion/babel-plugin@11.13.5': dependencies: - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/runtime': 7.26.0 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/runtime': 7.27.1 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -8704,9 +7023,9 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.0.4)(react@18.3.1)': + '@emotion/react@11.14.0(@types/react@19.1.4)(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 @@ -8716,7 +7035,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 18.3.1 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.1.4 transitivePeerDependencies: - supports-color @@ -8744,245 +7063,105 @@ snapshots: '@emotion/weak-memoize@0.4.0': {} - '@esbuild/aix-ppc64@0.24.2': - optional: true - - '@esbuild/aix-ppc64@0.25.0': - optional: true - - '@esbuild/android-arm64@0.24.2': - optional: true - - '@esbuild/android-arm64@0.25.0': - optional: true - - '@esbuild/android-arm@0.24.2': - optional: true - - '@esbuild/android-arm@0.25.0': - optional: true - - '@esbuild/android-x64@0.24.2': - optional: true - - '@esbuild/android-x64@0.25.0': - optional: true - - '@esbuild/darwin-arm64@0.24.2': - optional: true - - '@esbuild/darwin-arm64@0.25.0': - optional: true - - '@esbuild/darwin-x64@0.24.2': - optional: true - - '@esbuild/darwin-x64@0.25.0': - optional: true - - '@esbuild/freebsd-arm64@0.24.2': - optional: true - - '@esbuild/freebsd-arm64@0.25.0': - optional: true - - '@esbuild/freebsd-x64@0.24.2': - optional: true - - '@esbuild/freebsd-x64@0.25.0': - optional: true - - '@esbuild/linux-arm64@0.24.2': - optional: true - - '@esbuild/linux-arm64@0.25.0': - optional: true - - '@esbuild/linux-arm@0.24.2': - optional: true - - '@esbuild/linux-arm@0.25.0': - optional: true - - '@esbuild/linux-ia32@0.24.2': - optional: true - - '@esbuild/linux-ia32@0.25.0': - optional: true - - '@esbuild/linux-loong64@0.24.2': - optional: true - - '@esbuild/linux-loong64@0.25.0': - optional: true - - '@esbuild/linux-mips64el@0.24.2': - optional: true - - '@esbuild/linux-mips64el@0.25.0': + '@esbuild/aix-ppc64@0.25.4': optional: true - '@esbuild/linux-ppc64@0.24.2': + '@esbuild/android-arm64@0.25.4': optional: true - '@esbuild/linux-ppc64@0.25.0': + '@esbuild/android-arm@0.25.4': optional: true - '@esbuild/linux-riscv64@0.24.2': + '@esbuild/android-x64@0.25.4': optional: true - '@esbuild/linux-riscv64@0.25.0': + '@esbuild/darwin-arm64@0.25.4': optional: true - '@esbuild/linux-s390x@0.24.2': + '@esbuild/darwin-x64@0.25.4': optional: true - '@esbuild/linux-s390x@0.25.0': + '@esbuild/freebsd-arm64@0.25.4': optional: true - '@esbuild/linux-x64@0.24.2': + '@esbuild/freebsd-x64@0.25.4': optional: true - '@esbuild/linux-x64@0.25.0': + '@esbuild/linux-arm64@0.25.4': optional: true - '@esbuild/netbsd-arm64@0.24.2': + '@esbuild/linux-arm@0.25.4': optional: true - '@esbuild/netbsd-arm64@0.25.0': + '@esbuild/linux-ia32@0.25.4': optional: true - '@esbuild/netbsd-x64@0.24.2': + '@esbuild/linux-loong64@0.25.4': optional: true - '@esbuild/netbsd-x64@0.25.0': + '@esbuild/linux-mips64el@0.25.4': optional: true - '@esbuild/openbsd-arm64@0.24.2': + '@esbuild/linux-ppc64@0.25.4': optional: true - '@esbuild/openbsd-arm64@0.25.0': + '@esbuild/linux-riscv64@0.25.4': optional: true - '@esbuild/openbsd-x64@0.24.2': + '@esbuild/linux-s390x@0.25.4': optional: true - '@esbuild/openbsd-x64@0.25.0': + '@esbuild/linux-x64@0.25.4': optional: true - '@esbuild/sunos-x64@0.24.2': + '@esbuild/netbsd-arm64@0.25.4': optional: true - '@esbuild/sunos-x64@0.25.0': + '@esbuild/netbsd-x64@0.25.4': optional: true - '@esbuild/win32-arm64@0.24.2': + '@esbuild/openbsd-arm64@0.25.4': optional: true - '@esbuild/win32-arm64@0.25.0': + '@esbuild/openbsd-x64@0.25.4': optional: true - '@esbuild/win32-ia32@0.24.2': + '@esbuild/sunos-x64@0.25.4': optional: true - '@esbuild/win32-ia32@0.25.0': + '@esbuild/win32-arm64@0.25.4': optional: true - '@esbuild/win32-x64@0.24.2': + '@esbuild/win32-ia32@0.25.4': optional: true - '@esbuild/win32-x64@0.25.0': + '@esbuild/win32-x64@0.25.4': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/eslint-utils@4.7.0(eslint@9.27.0(jiti@2.4.2))': - dependencies: - eslint: 9.27.0(jiti@2.4.2) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.1': {} - - '@eslint/config-array@0.20.0': - dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1(supports-color@5.5.0) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.2.1': {} - - '@eslint/core@0.14.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.4.1(supports-color@5.5.0) - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/eslintrc@3.3.1': - dependencies: - ajv: 6.12.6 - debug: 4.4.1(supports-color@5.5.0) - espree: 10.3.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@8.57.1': {} - - '@eslint/js@9.27.0': {} - - '@eslint/object-schema@2.1.6': {} - - '@eslint/plugin-kit@0.3.1': - dependencies: - '@eslint/core': 0.14.0 - levn: 0.4.1 - - '@fastify/ajv-compiler@4.0.1': + '@fastify/ajv-compiler@4.0.2': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) fast-uri: 3.0.6 - '@fastify/error@4.0.0': {} + '@fastify/error@4.1.0': {} - '@fastify/fast-json-stringify-compiler@5.0.1': + '@fastify/fast-json-stringify-compiler@5.0.3': dependencies: - fast-json-stringify: 6.0.0 + fast-json-stringify: 6.0.1 '@fastify/forwarded@3.0.0': {} - '@fastify/merge-json-schemas@0.1.1': + '@fastify/merge-json-schemas@0.2.1': dependencies: - fast-deep-equal: 3.1.3 + dequal: 2.0.3 '@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 minimatch: 9.0.5 transitivePeerDependencies: - supports-color @@ -9002,26 +7181,26 @@ snapshots: type-is: 1.6.18 vary: 1.1.2 - '@floating-ui/core@1.6.9': + '@floating-ui/core@1.7.0': dependencies: '@floating-ui/utils': 0.2.9 - '@floating-ui/dom@1.6.13': + '@floating-ui/dom@1.7.0': dependencies: - '@floating-ui/core': 1.6.9 + '@floating-ui/core': 1.7.0 '@floating-ui/utils': 0.2.9 '@floating-ui/utils@0.2.9': {} - '@hello-pangea/dnd@16.6.0(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@hello-pangea/dnd@16.6.0(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 css-box-model: 1.2.1 memoize-one: 6.0.0 raf-schd: 4.0.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-redux: 8.1.3(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1) + react-redux: 8.1.3(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1) redux: 4.2.1 use-memo-one: 1.1.3(react@18.3.1) transitivePeerDependencies: @@ -9029,29 +7208,6 @@ snapshots: - '@types/react-dom' - react-native - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.6': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - - '@humanwhocodes/config-array@0.13.0': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.1(supports-color@5.5.0) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/object-schema@2.0.3': {} - - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.2': {} - '@hutson/parse-repository-url@3.0.2': {} '@hypnosphi/create-react-context@0.3.1(prop-types@15.8.1)(react@18.3.1)': @@ -9061,7 +7217,7 @@ snapshots: react: 18.3.1 warning: 4.0.3 - '@inquirer/figures@1.0.7': {} + '@inquirer/figures@1.0.11': {} '@isaacs/cliui@8.0.2': dependencies: @@ -9072,178 +7228,43 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.3': {} - '@jest/console@29.7.0': + '@jridgewell/gen-mapping@0.3.8': dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - jest-mock: 29.7.0 + '@jridgewell/resolve-uri@3.1.2': {} - '@jest/expect-utils@29.7.0': - dependencies: - jest-get-type: 29.6.3 + '@jridgewell/set-array@1.2.1': {} - '@jest/expect@29.7.0': + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 - '@jest/fake-timers@29.7.0': + '@jridgewell/trace-mapping@0.3.9': dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.18 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@lukeed/ms@2.0.2': {} - '@jest/globals@29.7.0': + '@mikro-orm/core@6.4.15': dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color + dataloader: 2.2.3 + dotenv: 16.5.0 + esprima: 4.0.1 + fs-extra: 11.3.0 + globby: 11.1.0 + mikro-orm: 6.4.15 + reflect-metadata: 0.2.2 - '@jest/reporters@29.7.0': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.15.18 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.7 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.26.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.18 - '@types/yargs': 17.0.33 - chalk: 4.1.2 - - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@lukeed/ms@2.0.2': {} - - '@mikro-orm/core@6.4.15': - dependencies: - dataloader: 2.2.3 - dotenv: 16.5.0 - esprima: 4.0.1 - fs-extra: 11.3.0 - globby: 11.1.0 - mikro-orm: 6.4.15 - reflect-metadata: 0.2.2 - - '@mikro-orm/knex@6.4.15(@mikro-orm/core@6.4.15)(pg@8.15.6)': + '@mikro-orm/knex@6.4.15(@mikro-orm/core@6.4.15)(pg@8.15.6)': dependencies: '@mikro-orm/core': 6.4.15 fs-extra: 11.3.0 @@ -9283,26 +7304,20 @@ snapshots: globby: 11.1.0 ts-morph: 25.0.1 - '@napi-rs/wasm-runtime@0.2.4': - dependencies: - '@emnapi/core': 1.3.1 - '@emnapi/runtime': 1.3.1 - '@tybys/wasm-util': 0.9.0 - - '@napi-rs/wasm-runtime@0.2.7': + '@napi-rs/wasm-runtime@0.2.9': dependencies: - '@emnapi/core': 1.3.1 - '@emnapi/runtime': 1.3.1 + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 '@tybys/wasm-util': 0.9.0 optional: true - '@noble/ciphers@1.0.0': {} + '@noble/ciphers@1.3.0': {} - '@noble/curves@1.6.0': + '@noble/curves@1.9.0': dependencies: - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.8.0 - '@noble/hashes@1.5.0': {} + '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -9316,216 +7331,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@nx/devkit@21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))': - dependencies: - ejs: 3.1.10 - enquirer: 2.3.6 - ignore: 5.3.2 - minimatch: 9.0.3 - nx: 21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1) - semver: 7.7.1 - tmp: 0.2.3 - tslib: 2.8.1 - yargs-parser: 21.1.1 - - '@nx/eslint@21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@zkochan/js-yaml@0.0.7)(debug@4.4.1)(eslint@9.27.0(jiti@2.4.2))(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))': - dependencies: - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/js': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - eslint: 9.27.0(jiti@2.4.2) - semver: 7.7.1 - tslib: 2.8.1 - typescript: 5.7.3 - optionalDependencies: - '@zkochan/js-yaml': 0.0.7 - transitivePeerDependencies: - - '@babel/traverse' - - '@swc-node/register' - - '@swc/core' - - debug - - nx - - supports-color - - verdaccio - - '@nx/jest@21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))(ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3))(typescript@5.8.3)': - dependencies: - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/js': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@phenomnomnominal/tsquery': 5.0.1(typescript@5.8.3) - identity-obj-proxy: 3.0.0 - jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3)) - jest-resolve: 29.7.0 - jest-util: 29.7.0 - minimatch: 9.0.3 - picocolors: 1.1.1 - resolve.exports: 2.0.3 - semver: 7.7.1 - tslib: 2.8.1 - yargs-parser: 21.1.1 - transitivePeerDependencies: - - '@babel/traverse' - - '@swc-node/register' - - '@swc/core' - - '@types/node' - - babel-plugin-macros - - debug - - node-notifier - - nx - - supports-color - - ts-node - - typescript - - verdaccio - - '@nx/js@21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))': - dependencies: - '@babel/core': 7.26.0 - '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.0) - '@babel/preset-env': 7.26.0(@babel/core@7.26.0) - '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) - '@babel/runtime': 7.26.0 - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/workspace': 21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1) - '@zkochan/js-yaml': 0.0.7 - babel-plugin-const-enum: 1.2.0(@babel/core@7.26.0) - babel-plugin-macros: 3.1.0 - babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.26.0)(@babel/traverse@7.26.5) - chalk: 4.1.2 - columnify: 1.6.0 - detect-port: 1.6.1 - enquirer: 2.3.6 - ignore: 5.3.2 - js-tokens: 4.0.0 - jsonc-parser: 3.2.0 - npm-package-arg: 11.0.1 - npm-run-path: 4.0.1 - ora: 5.3.0 - picocolors: 1.1.1 - picomatch: 4.0.2 - semver: 7.7.1 - source-map-support: 0.5.19 - tinyglobby: 0.2.13 - tslib: 2.8.1 - transitivePeerDependencies: - - '@babel/traverse' - - '@swc-node/register' - - '@swc/core' - - debug - - nx - - supports-color - - '@nx/node@21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(@zkochan/js-yaml@0.0.7)(babel-plugin-macros@3.1.0)(debug@4.4.1)(eslint@9.27.0(jiti@2.4.2))(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))(ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3))(typescript@5.8.3)': - dependencies: - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/eslint': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@zkochan/js-yaml@0.0.7)(debug@4.4.1)(eslint@9.27.0(jiti@2.4.2))(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/jest': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))(ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3))(typescript@5.8.3) - '@nx/js': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - kill-port: 1.6.1 - tcp-port-used: 1.0.2 - tslib: 2.8.1 - transitivePeerDependencies: - - '@babel/traverse' - - '@swc-node/register' - - '@swc/core' - - '@types/node' - - '@zkochan/js-yaml' - - babel-plugin-macros - - debug - - eslint - - node-notifier - - nx - - supports-color - - ts-node - - typescript - - verdaccio - - '@nx/nx-darwin-arm64@21.0.3': - optional: true - - '@nx/nx-darwin-x64@21.0.3': - optional: true - - '@nx/nx-freebsd-x64@21.0.3': - optional: true - - '@nx/nx-linux-arm-gnueabihf@21.0.3': - optional: true - - '@nx/nx-linux-arm64-gnu@21.0.3': - optional: true - - '@nx/nx-linux-arm64-musl@21.0.3': - optional: true - - '@nx/nx-linux-x64-gnu@21.0.3': - optional: true - - '@nx/nx-linux-x64-musl@21.0.3': - optional: true - - '@nx/nx-win32-arm64-msvc@21.0.3': - optional: true - - '@nx/nx-win32-x64-msvc@21.0.3': - optional: true - - '@nx/vite@21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1))(vitest@3.1.3)': - dependencies: - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/js': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@phenomnomnominal/tsquery': 5.0.1(typescript@5.8.3) - '@swc/helpers': 0.5.17 - enquirer: 2.3.6 - picomatch: 4.0.2 - semver: 7.7.1 - tsconfig-paths: 4.2.0 - vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) - vitest: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) - transitivePeerDependencies: - - '@babel/traverse' - - '@swc-node/register' - - '@swc/core' - - debug - - nx - - supports-color - - typescript - - verdaccio - - '@nx/web@21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1))': - dependencies: - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@nx/js': 21.0.3(@babel/traverse@7.26.5)(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - detect-port: 1.6.1 - http-server: 14.1.1(debug@4.4.1) - picocolors: 1.1.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@babel/traverse' - - '@swc-node/register' - - '@swc/core' - - debug - - nx - - supports-color - - verdaccio - - '@nx/workspace@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)': - dependencies: - '@nx/devkit': 21.0.3(nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1)) - '@zkochan/js-yaml': 0.0.7 - chalk: 4.1.2 - enquirer: 2.3.6 - nx: 21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1) - picomatch: 4.0.2 - tslib: 2.8.1 - yargs-parser: 21.1.1 - transitivePeerDependencies: - - '@swc-node/register' - - '@swc/core' - - debug - '@opentelemetry/api-logs@0.57.2': dependencies: '@opentelemetry/api': 1.9.0 @@ -9546,7 +7351,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9555,7 +7360,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color @@ -9572,7 +7377,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9603,7 +7408,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9614,7 +7419,7 @@ snapshots: '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 forwarded-parse: 2.1.2 - semver: 7.7.1 + semver: 7.7.2 transitivePeerDependencies: - supports-color @@ -9623,7 +7428,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9631,7 +7436,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9639,7 +7444,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9648,7 +7453,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9663,7 +7468,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9672,7 +7477,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9680,7 +7485,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -9689,7 +7494,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@types/mysql': 2.15.26 transitivePeerDependencies: - supports-color @@ -9699,7 +7504,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) '@types/pg': 8.6.1 '@types/pg-pool': 2.0.6 @@ -9711,7 +7516,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 transitivePeerDependencies: - supports-color @@ -9719,7 +7524,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color @@ -9738,8 +7543,8 @@ snapshots: '@opentelemetry/api-logs': 0.57.2 '@types/shimmer': 1.2.0 import-in-the-middle: 1.13.2 - require-in-the-middle: 7.4.0 - semver: 7.7.1 + require-in-the-middle: 7.5.2 + semver: 7.7.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color @@ -9761,59 +7566,58 @@ snapshots: '@opentelemetry/semantic-conventions@1.28.0': {} - '@opentelemetry/semantic-conventions@1.30.0': {} + '@opentelemetry/semantic-conventions@1.33.0': {} '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@oxc-resolver/binding-darwin-arm64@5.0.0': + '@oxc-resolver/binding-darwin-arm64@5.3.0': optional: true - '@oxc-resolver/binding-darwin-x64@5.0.0': + '@oxc-resolver/binding-darwin-x64@5.3.0': optional: true - '@oxc-resolver/binding-freebsd-x64@5.0.0': + '@oxc-resolver/binding-freebsd-x64@5.3.0': optional: true - '@oxc-resolver/binding-linux-arm-gnueabihf@5.0.0': + '@oxc-resolver/binding-linux-arm-gnueabihf@5.3.0': optional: true - '@oxc-resolver/binding-linux-arm64-gnu@5.0.0': + '@oxc-resolver/binding-linux-arm64-gnu@5.3.0': optional: true - '@oxc-resolver/binding-linux-arm64-musl@5.0.0': + '@oxc-resolver/binding-linux-arm64-musl@5.3.0': optional: true - '@oxc-resolver/binding-linux-x64-gnu@5.0.0': + '@oxc-resolver/binding-linux-riscv64-gnu@5.3.0': optional: true - '@oxc-resolver/binding-linux-x64-musl@5.0.0': + '@oxc-resolver/binding-linux-s390x-gnu@5.3.0': optional: true - '@oxc-resolver/binding-wasm32-wasi@5.0.0': - dependencies: - '@napi-rs/wasm-runtime': 0.2.7 + '@oxc-resolver/binding-linux-x64-gnu@5.3.0': optional: true - '@oxc-resolver/binding-win32-arm64-msvc@5.0.0': + '@oxc-resolver/binding-linux-x64-musl@5.3.0': optional: true - '@oxc-resolver/binding-win32-x64-msvc@5.0.0': + '@oxc-resolver/binding-wasm32-wasi@5.3.0': + dependencies: + '@napi-rs/wasm-runtime': 0.2.9 optional: true - '@phenomnomnominal/tsquery@5.0.1(typescript@5.8.3)': - dependencies: - esquery: 1.6.0 - typescript: 5.8.3 + '@oxc-resolver/binding-win32-arm64-msvc@5.3.0': + optional: true - '@pkgjs/parseargs@0.11.0': + '@oxc-resolver/binding-win32-x64-msvc@5.3.0': optional: true - '@pkgr/core@0.2.0': {} + '@pkgjs/parseargs@0.11.0': + optional: true - '@polka/url@1.0.0-next.28': {} + '@polka/url@1.0.0-next.29': {} '@popperjs/core@2.11.8': {} @@ -9826,186 +7630,138 @@ snapshots: '@redux-devtools/extension@3.3.0(redux@4.2.1)': dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 immutable: 4.3.7 redux: 4.2.1 '@remirror/core-constants@2.0.2': {} - '@remix-run/router@1.21.0': {} + '@remix-run/router@1.23.0': {} - '@rollup/plugin-babel@6.0.4(@babel/core@7.26.0)(@types/babel__core@7.20.5)(rollup@4.24.0)': + '@rollup/plugin-babel@6.0.4(@babel/core@7.27.1)(@types/babel__core@7.20.5)(rollup@4.40.2)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@rollup/pluginutils': 5.1.4(rollup@4.24.0) + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) optionalDependencies: '@types/babel__core': 7.20.5 - rollup: 4.24.0 + rollup: 4.40.2 transitivePeerDependencies: - supports-color - '@rollup/plugin-commonjs@25.0.8(rollup@4.24.0)': + '@rollup/plugin-commonjs@25.0.8(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.24.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) commondir: 1.0.1 estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 magic-string: 0.30.17 optionalDependencies: - rollup: 4.24.0 + rollup: 4.40.2 - '@rollup/plugin-inject@5.0.5(rollup@4.24.0)': + '@rollup/plugin-inject@5.0.5(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.24.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) estree-walker: 2.0.2 magic-string: 0.30.17 optionalDependencies: - rollup: 4.24.0 + rollup: 4.40.2 - '@rollup/plugin-json@6.1.0(rollup@4.24.0)': + '@rollup/plugin-json@6.1.0(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.24.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) optionalDependencies: - rollup: 4.24.0 + rollup: 4.40.2 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.24.0)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.24.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.10 optionalDependencies: - rollup: 4.24.0 + rollup: 4.40.2 - '@rollup/plugin-replace@5.0.7(rollup@4.24.0)': + '@rollup/plugin-replace@5.0.7(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.24.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) magic-string: 0.30.17 optionalDependencies: - rollup: 4.24.0 + rollup: 4.40.2 - '@rollup/pluginutils@5.1.4(rollup@4.24.0)': + '@rollup/pluginutils@5.1.4(rollup@4.40.2)': dependencies: '@types/estree': 1.0.7 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.24.0 - - '@rollup/rollup-android-arm-eabi@4.24.0': - optional: true - - '@rollup/rollup-android-arm-eabi@4.40.0': - optional: true - - '@rollup/rollup-android-arm64@4.24.0': - optional: true - - '@rollup/rollup-android-arm64@4.40.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.24.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.40.0': - optional: true - - '@rollup/rollup-darwin-x64@4.24.0': - optional: true - - '@rollup/rollup-darwin-x64@4.40.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.40.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.40.0': - optional: true + rollup: 4.40.2 - '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + '@rollup/rollup-android-arm-eabi@4.40.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.40.0': + '@rollup/rollup-android-arm64@4.40.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.24.0': + '@rollup/rollup-darwin-arm64@4.40.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.40.0': + '@rollup/rollup-darwin-x64@4.40.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.24.0': + '@rollup/rollup-freebsd-arm64@4.40.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.40.0': + '@rollup/rollup-freebsd-x64@4.40.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.24.0': + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.40.0': + '@rollup/rollup-linux-arm-musleabihf@4.40.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.40.0': + '@rollup/rollup-linux-arm64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + '@rollup/rollup-linux-arm64-musl@4.40.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.24.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.40.0': + '@rollup/rollup-linux-riscv64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.40.0': + '@rollup/rollup-linux-riscv64-musl@4.40.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.24.0': + '@rollup/rollup-linux-s390x-gnu@4.40.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.40.0': + '@rollup/rollup-linux-x64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.24.0': + '@rollup/rollup-linux-x64-musl@4.40.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.40.0': + '@rollup/rollup-win32-arm64-msvc@4.40.2': optional: true - '@rollup/rollup-linux-x64-musl@4.24.0': + '@rollup/rollup-win32-ia32-msvc@4.40.2': optional: true - '@rollup/rollup-linux-x64-musl@4.40.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.24.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.40.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.24.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.40.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.24.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.40.0': + '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true '@sentry-internal/node-cpu-profiler@2.2.0': dependencies: - detect-libc: 2.0.3 + detect-libc: 2.0.4 node-abi: 3.75.0 '@sentry/cli-darwin@2.45.0': @@ -10085,22 +7841,22 @@ snapshots: '@opentelemetry/instrumentation-undici': 0.10.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@prisma/instrumentation': 6.7.0(@opentelemetry/api@1.9.0) '@sentry/core': 9.19.0 - '@sentry/opentelemetry': 9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0) + '@sentry/opentelemetry': 9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.33.0) import-in-the-middle: 1.13.2 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)': + '@sentry/opentelemetry@9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.33.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.30.0 + '@opentelemetry/semantic-conventions': 1.33.0 '@sentry/core': 9.19.0 '@sentry/profiling-node@9.19.0': @@ -10111,16 +7867,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@sinclair/typebox@0.27.8': {} - - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - '@styled-system/background@5.1.2': dependencies: '@styled-system/core': 5.1.2 @@ -10172,20 +7918,20 @@ snapshots: '@styled-system/core': 5.1.2 '@styled-system/css': 5.1.5 - '@swc-node/core@1.13.3(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)': + '@swc-node/core@1.13.3(@swc/core@1.11.24(@swc/helpers@0.5.17))(@swc/types@0.1.21)': dependencies: - '@swc/core': 1.11.22(@swc/helpers@0.5.17) + '@swc/core': 1.11.24(@swc/helpers@0.5.17) '@swc/types': 0.1.21 - '@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3)': + '@swc-node/register@1.10.10(@swc/core@1.11.24(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3)': dependencies: - '@swc-node/core': 1.13.3(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21) + '@swc-node/core': 1.13.3(@swc/core@1.11.24(@swc/helpers@0.5.17))(@swc/types@0.1.21) '@swc-node/sourcemap-support': 0.5.1 - '@swc/core': 1.11.22(@swc/helpers@0.5.17) + '@swc/core': 1.11.24(@swc/helpers@0.5.17) colorette: 2.0.20 debug: 4.4.1(supports-color@5.5.0) - oxc-resolver: 5.0.0 - pirates: 4.0.6 + oxc-resolver: 5.3.0 + pirates: 4.0.7 tslib: 2.8.1 typescript: 5.8.3 transitivePeerDependencies: @@ -10197,51 +7943,51 @@ snapshots: source-map-support: 0.5.21 tslib: 2.8.1 - '@swc/core-darwin-arm64@1.11.22': + '@swc/core-darwin-arm64@1.11.24': optional: true - '@swc/core-darwin-x64@1.11.22': + '@swc/core-darwin-x64@1.11.24': optional: true - '@swc/core-linux-arm-gnueabihf@1.11.22': + '@swc/core-linux-arm-gnueabihf@1.11.24': optional: true - '@swc/core-linux-arm64-gnu@1.11.22': + '@swc/core-linux-arm64-gnu@1.11.24': optional: true - '@swc/core-linux-arm64-musl@1.11.22': + '@swc/core-linux-arm64-musl@1.11.24': optional: true - '@swc/core-linux-x64-gnu@1.11.22': + '@swc/core-linux-x64-gnu@1.11.24': optional: true - '@swc/core-linux-x64-musl@1.11.22': + '@swc/core-linux-x64-musl@1.11.24': optional: true - '@swc/core-win32-arm64-msvc@1.11.22': + '@swc/core-win32-arm64-msvc@1.11.24': optional: true - '@swc/core-win32-ia32-msvc@1.11.22': + '@swc/core-win32-ia32-msvc@1.11.24': optional: true - '@swc/core-win32-x64-msvc@1.11.22': + '@swc/core-win32-x64-msvc@1.11.24': optional: true - '@swc/core@1.11.22(@swc/helpers@0.5.17)': + '@swc/core@1.11.24(@swc/helpers@0.5.17)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.21 optionalDependencies: - '@swc/core-darwin-arm64': 1.11.22 - '@swc/core-darwin-x64': 1.11.22 - '@swc/core-linux-arm-gnueabihf': 1.11.22 - '@swc/core-linux-arm64-gnu': 1.11.22 - '@swc/core-linux-arm64-musl': 1.11.22 - '@swc/core-linux-x64-gnu': 1.11.22 - '@swc/core-linux-x64-musl': 1.11.22 - '@swc/core-win32-arm64-msvc': 1.11.22 - '@swc/core-win32-ia32-msvc': 1.11.22 - '@swc/core-win32-x64-msvc': 1.11.22 + '@swc/core-darwin-arm64': 1.11.24 + '@swc/core-darwin-x64': 1.11.24 + '@swc/core-linux-arm-gnueabihf': 1.11.24 + '@swc/core-linux-arm64-gnu': 1.11.24 + '@swc/core-linux-arm64-musl': 1.11.24 + '@swc/core-linux-x64-gnu': 1.11.24 + '@swc/core-linux-x64-musl': 1.11.24 + '@swc/core-win32-arm64-msvc': 1.11.24 + '@swc/core-win32-ia32-msvc': 1.11.24 + '@swc/core-win32-x64-msvc': 1.11.24 '@swc/helpers': 0.5.17 '@swc/counter@0.1.3': {} @@ -10259,17 +8005,17 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tinymce: 7.9.0 + tinymce: 6.8.5 '@tiptap/core@2.1.13(@tiptap/pm@2.1.13)': dependencies: '@tiptap/pm': 2.1.13 - '@tiptap/extension-blockquote@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-blockquote@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-bold@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-bold@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) @@ -10279,18 +8025,16 @@ snapshots: '@tiptap/pm': 2.1.13 tippy.js: 6.3.7 - '@tiptap/extension-bullet-list@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))': + '@tiptap/extension-bullet-list@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-text-style': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) '@tiptap/extension-character-count@2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 - '@tiptap/extension-code-block@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': + '@tiptap/extension-code-block@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 @@ -10303,7 +8047,7 @@ snapshots: dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-dropcursor@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': + '@tiptap/extension-dropcursor@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 @@ -10314,12 +8058,12 @@ snapshots: '@tiptap/pm': 2.1.13 tippy.js: 6.3.7 - '@tiptap/extension-gapcursor@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': + '@tiptap/extension-gapcursor@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 - '@tiptap/extension-hard-break@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-hard-break@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) @@ -10327,12 +8071,12 @@ snapshots: dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-history@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': + '@tiptap/extension-history@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 - '@tiptap/extension-horizontal-rule@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': + '@tiptap/extension-horizontal-rule@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 @@ -10341,7 +8085,7 @@ snapshots: dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-italic@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-italic@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) @@ -10349,23 +8093,21 @@ snapshots: dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) '@tiptap/pm': 2.1.13 - linkifyjs: 4.2.0 + linkifyjs: 4.3.1 - '@tiptap/extension-list-item@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-list-item@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-ordered-list@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))': + '@tiptap/extension-ordered-list@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-text-style': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-paragraph@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-paragraph@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-strike@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': + '@tiptap/extension-strike@2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) @@ -10390,10 +8132,6 @@ snapshots: dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': - dependencies: - '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-text@2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) @@ -10404,24 +8142,24 @@ snapshots: '@tiptap/pm@2.1.13': dependencies: - prosemirror-changeset: 2.2.1 + prosemirror-changeset: 2.3.0 prosemirror-collab: 1.3.1 - prosemirror-commands: 1.6.1 - prosemirror-dropcursor: 1.8.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 prosemirror-gapcursor: 1.3.2 prosemirror-history: 1.4.1 - prosemirror-inputrules: 1.4.0 - prosemirror-keymap: 1.2.2 - prosemirror-markdown: 1.13.1 - prosemirror-menu: 1.2.4 - prosemirror-model: 1.23.0 - prosemirror-schema-basic: 1.2.3 - prosemirror-schema-list: 1.4.1 + prosemirror-inputrules: 1.5.0 + prosemirror-keymap: 1.2.3 + prosemirror-markdown: 1.13.2 + prosemirror-menu: 1.2.5 + prosemirror-model: 1.25.1 + prosemirror-schema-basic: 1.2.4 + prosemirror-schema-list: 1.5.1 prosemirror-state: 1.4.3 - prosemirror-tables: 1.5.0 - prosemirror-trailing-node: 2.0.9(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3) - prosemirror-transform: 1.10.2 - prosemirror-view: 1.34.3 + prosemirror-tables: 1.7.1 + prosemirror-trailing-node: 2.0.9(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.2) + prosemirror-transform: 1.10.4 + prosemirror-view: 1.39.2 '@tiptap/react@2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -10432,34 +8170,33 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tiptap/starter-kit@2.1.13(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@tiptap/pm@2.1.13)': + '@tiptap/starter-kit@2.1.13(@tiptap/pm@2.1.13)': dependencies: '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13) - '@tiptap/extension-blockquote': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-bold': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-bullet-list': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))) + '@tiptap/extension-blockquote': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-bold': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-bullet-list': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) '@tiptap/extension-code': 2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-code-block': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) + '@tiptap/extension-code-block': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) '@tiptap/extension-document': 2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-dropcursor': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) - '@tiptap/extension-gapcursor': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) - '@tiptap/extension-hard-break': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-dropcursor': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) + '@tiptap/extension-gapcursor': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) + '@tiptap/extension-hard-break': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) '@tiptap/extension-heading': 2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-history': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) - '@tiptap/extension-horizontal-rule': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) - '@tiptap/extension-italic': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-ordered-list': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))) - '@tiptap/extension-paragraph': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) - '@tiptap/extension-strike': 2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-history': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) + '@tiptap/extension-horizontal-rule': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13))(@tiptap/pm@2.1.13) + '@tiptap/extension-italic': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-list-item': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-ordered-list': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-paragraph': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) + '@tiptap/extension-strike': 2.12.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) '@tiptap/extension-text': 2.1.13(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)) transitivePeerDependencies: - - '@tiptap/extension-text-style' - '@tiptap/pm' - '@ts-morph/common@0.26.0': + '@ts-morph/common@0.26.1': dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.3 minimatch: 9.0.5 path-browserify: 1.0.1 @@ -10478,27 +8215,32 @@ snapshots: '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 + optional: true '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 - '@types/babel__generator': 7.6.8 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 + '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 + '@types/babel__traverse': 7.20.7 + optional: true - '@types/babel__generator@7.6.8': + '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.27.1 + optional: true '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 + optional: true - '@types/babel__traverse@7.20.6': + '@types/babel__traverse@7.20.7': dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.27.1 + optional: true '@types/bcrypt@5.0.2': dependencies: @@ -10508,11 +8250,13 @@ snapshots: dependencies: '@types/deep-eql': 4.0.2 + '@types/config@3.3.5': {} + '@types/connect@3.4.38': dependencies: '@types/node': 22.15.18 - '@types/conventional-commits-parser@5.0.0': + '@types/conventional-commits-parser@5.0.1': dependencies: '@types/node': 22.15.18 @@ -10526,31 +8270,13 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.6': {} - '@types/estree@1.0.7': {} - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 22.15.18 - '@types/hoist-non-react-statics@3.3.6': dependencies: - '@types/react': 19.0.4 + '@types/react': 19.1.4 hoist-non-react-statics: 3.3.2 - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/json-schema@7.0.15': {} - '@types/linkify-it@5.0.0': {} '@types/markdown-it@14.1.2': @@ -10592,11 +8318,11 @@ snapshots: pg-protocol: 1.10.0 pg-types: 2.2.0 - '@types/react-transition-group@4.4.12(@types/react@19.0.4)': + '@types/react-transition-group@4.4.12(@types/react@19.1.4)': dependencies: - '@types/react': 19.0.4 + '@types/react': 19.1.4 - '@types/react@19.0.4': + '@types/react@19.1.4': dependencies: csstype: 3.1.3 @@ -10615,8 +8341,6 @@ snapshots: '@types/sinonjs__fake-timers@8.1.5': {} - '@types/stack-utils@2.0.3': {} - '@types/tedious@4.0.14': dependencies: '@types/node': 22.15.18 @@ -10625,150 +8349,25 @@ snapshots: '@types/validator@13.12.2': {} - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.33': - dependencies: - '@types/yargs-parser': 21.0.3 - - '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/type-utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.32.1 - eslint: 9.27.0(jiti@2.4.2) - graphemer: 1.4.0 - ignore: 7.0.4 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3)': + '@vitest/coverage-v8@3.1.3(vitest@3.1.3)': dependencies: - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 6.21.0 + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 debug: 4.4.1(supports-color@5.5.0) - eslint: 8.57.1 - optionalDependencies: - typescript: 5.8.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.9.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.1(supports-color@5.5.0) - eslint: 9.27.0(jiti@2.4.2) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@6.21.0': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - - '@typescript-eslint/scope-manager@8.32.1': - dependencies: - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/visitor-keys': 8.32.1 - - '@typescript-eslint/type-utils@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) - debug: 4.4.1(supports-color@5.5.0) - eslint: 9.27.0(jiti@2.4.2) - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@6.21.0': {} - - '@typescript-eslint/types@8.32.1': {} - - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.8.3)': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.4.1(supports-color@5.5.0) - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.7.1 - ts-api-utils: 1.3.0(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.32.1(typescript@5.8.3)': - dependencies: - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/visitor-keys': 8.32.1 - debug: 4.4.1(supports-color@5.5.0) - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.1 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/types': 8.32.1 - '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) - eslint: 9.27.0(jiti@2.4.2) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@6.21.0': - dependencies: - '@typescript-eslint/types': 6.21.0 - eslint-visitor-keys: 3.4.3 - - '@typescript-eslint/visitor-keys@8.32.1': - dependencies: - '@typescript-eslint/types': 8.32.1 - eslint-visitor-keys: 4.2.0 - - '@ungap/structured-clone@1.2.0': {} - - '@vitest/coverage-v8@3.1.3(vitest@3.1.3)': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.9.0 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.18)(@vitest/ui@3.1.3)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@3.1.3': + '@vitest/expect@3.1.3': dependencies: '@vitest/spy': 3.1.3 '@vitest/utils': 3.1.3 @@ -10819,17 +8418,6 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 - '@yarnpkg/lockfile@1.1.0': {} - - '@yarnpkg/parsers@3.0.2': - dependencies: - js-yaml: 3.14.1 - tslib: 2.8.1 - - '@zkochan/js-yaml@0.0.7': - dependencies: - argparse: 2.0.1 - JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -10837,73 +8425,66 @@ snapshots: abstract-logging@2.0.1: {} - acorn-import-attributes@1.9.5(acorn@8.14.0): - dependencies: - acorn: 8.14.0 - - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-import-attributes@1.9.5(acorn@8.14.1): dependencies: - acorn: 8.14.0 + acorn: 8.14.1 acorn-walk@8.3.4: dependencies: - acorn: 8.12.1 - - acorn@8.12.1: {} + acorn: 8.14.1 - acorn@8.14.0: {} + acorn@8.14.1: {} add-stream@1.0.0: {} address@1.2.2: {} - adminjs@7.8.15(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@types/babel__core@7.20.5)(@types/react@19.0.4)(debug@4.4.1): - dependencies: - '@adminjs/design-system': 4.1.1(@babel/core@7.26.0)(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.1.13(@tiptap/pm@2.1.13)))(@types/react@19.0.4)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1) - '@babel/core': 7.26.0 - '@babel/parser': 7.26.5 - '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.0) - '@babel/preset-env': 7.26.0(@babel/core@7.26.0) - '@babel/preset-react': 7.26.3(@babel/core@7.26.0) - '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) - '@babel/register': 7.25.9(@babel/core@7.26.0) - '@hello-pangea/dnd': 16.6.0(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + adminjs@7.8.15(@types/babel__core@7.20.5)(@types/react@19.1.4)(debug@4.4.1): + dependencies: + '@adminjs/design-system': 4.1.1(@babel/core@7.27.1)(@types/react@19.1.4)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1) + '@babel/core': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-runtime': 7.27.1(@babel/core@7.27.1) + '@babel/preset-env': 7.27.2(@babel/core@7.27.1) + '@babel/preset-react': 7.27.1(@babel/core@7.27.1) + '@babel/preset-typescript': 7.27.1(@babel/core@7.27.1) + '@babel/register': 7.27.1(@babel/core@7.27.1) + '@hello-pangea/dnd': 16.6.0(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@redux-devtools/extension': 3.3.0(redux@4.2.1) - '@rollup/plugin-babel': 6.0.4(@babel/core@7.26.0)(@types/babel__core@7.20.5)(rollup@4.24.0) - '@rollup/plugin-commonjs': 25.0.8(rollup@4.24.0) - '@rollup/plugin-json': 6.1.0(rollup@4.24.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.24.0) - '@rollup/plugin-replace': 5.0.7(rollup@4.24.0) - axios: 1.7.9(debug@4.4.1) + '@rollup/plugin-babel': 6.0.4(@babel/core@7.27.1)(@types/babel__core@7.20.5)(rollup@4.40.2) + '@rollup/plugin-commonjs': 25.0.8(rollup@4.40.2) + '@rollup/plugin-json': 6.1.0(rollup@4.40.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.40.2) + '@rollup/plugin-replace': 5.0.7(rollup@4.40.2) + axios: 1.9.0(debug@4.4.1) commander: 10.0.1 flat: 5.0.2 i18next: 22.5.1 i18next-browser-languagedetector: 7.2.2 - i18next-http-backend: 2.7.1 + i18next-http-backend: 2.7.3 lodash: 4.17.21 ora: 6.3.1 prop-types: 15.8.1 punycode: 2.3.1 - qs: 6.13.1 + qs: 6.14.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-feather: 2.0.10(react@18.3.1) react-i18next: 12.3.1(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-is: 18.3.1 - react-redux: 8.1.3(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1) - react-router: 6.28.1(react@18.3.1) - react-router-dom: 6.28.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-redux: 8.1.3(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1) + react-router: 6.30.0(react@18.3.1) + react-router-dom: 6.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) redux: 4.2.1 regenerator-runtime: 0.14.1 - rollup: 4.24.0 - rollup-plugin-esbuild-minify: 1.2.0(rollup@4.24.0) - rollup-plugin-polyfill-node: 0.13.0(rollup@4.24.0) + rollup: 4.40.2 + rollup-plugin-esbuild-minify: 1.3.0(rollup@4.40.2) + rollup-plugin-polyfill-node: 0.13.0(rollup@4.40.2) slash: 5.1.0 uuid: 9.0.1 xss: 1.0.15 transitivePeerDependencies: - - '@tiptap/extension-text-style' - '@types/babel__core' - '@types/react' - '@types/react-dom' @@ -10922,13 +8503,6 @@ snapshots: optionalDependencies: ajv: 8.17.1 - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -10936,8 +8510,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-colors@4.1.3: {} - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -10946,14 +8518,10 @@ snapshots: dependencies: environment: 1.1.0 - ansi-regex@2.1.1: {} - ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} - ansi-styles@2.2.1: {} - ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -10962,29 +8530,18 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} any-promise@1.3.0: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - arg@4.1.3: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} - array-buffer-byte-length@1.0.1: + array-buffer-byte-length@1.0.2: dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 + call-bound: 1.0.4 + is-array-buffer: 3.0.5 array-ify@1.0.0: {} @@ -10992,166 +8549,77 @@ snapshots: arrify@1.0.1: {} - assert-never@1.3.0: {} + assert-never@1.4.0: {} assertion-error@2.0.1: {} - async@2.6.4: - dependencies: - lodash: 4.17.21 - - async@3.2.6: {} - asynckit@0.4.0: {} atomic-sleep@1.0.0: {} available-typed-arrays@1.0.7: dependencies: - possible-typed-array-names: 1.0.0 + possible-typed-array-names: 1.1.0 - avvio@9.0.0: + avvio@9.1.0: dependencies: - '@fastify/error': 4.0.0 + '@fastify/error': 4.1.0 fastq: 1.19.1 - axios@1.7.9(debug@4.4.1): + axios@1.9.0(debug@4.4.1): dependencies: follow-redirects: 1.15.9(debug@4.4.1) - form-data: 4.0.1 + form-data: 4.0.2 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - axios@1.8.3(debug@4.4.1): - dependencies: - follow-redirects: 1.15.9(debug@4.4.1) - form-data: 4.0.1 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - babel-jest@29.7.0(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.26.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-const-enum@1.2.0(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.5(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.26.5 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.5 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 - babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 cosmiconfig: 7.1.0 - resolve: 1.22.8 + resolve: 1.22.10 - babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): + babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.27.1): dependencies: - '@babel/compat-data': 7.26.5 - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + '@babel/compat-data': 7.27.2 + '@babel/core': 7.27.1 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.1) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.1): dependencies: - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) - core-js-compat: 3.40.0 + '@babel/core': 7.27.1 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.1) + core-js-compat: 3.42.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0): + babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.27.1): dependencies: - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + '@babel/core': 7.27.1 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - babel-plugin-styled-components@2.1.4(@babel/core@7.26.0)(styled-components@5.3.9(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1))(supports-color@5.5.0): + babel-plugin-styled-components@2.1.4(@babel/core@7.27.1)(styled-components@5.3.9(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1))(supports-color@5.5.0): dependencies: - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.1) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.9(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1) + styled-components: 5.3.9(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@babel/core' - supports-color - babel-plugin-transform-typescript-metadata@0.3.2(@babel/core@7.26.0)(@babel/traverse@7.26.5): - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - optionalDependencies: - '@babel/traverse': 7.26.5(supports-color@5.5.0) - - babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) - - babel-preset-jest@29.6.3(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) - balanced-match@1.0.2: {} base64-js@1.5.1: {} - basic-auth@2.0.1: - dependencies: - safe-buffer: 5.1.2 - bcrypt@6.0.0: dependencies: node-addon-api: 8.3.1 @@ -11182,16 +8650,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: + browserslist@4.24.5: dependencies: - caniuse-lite: 1.0.30001692 - electron-to-chromium: 1.5.80 + caniuse-lite: 1.0.30001718 + electron-to-chromium: 1.5.152 node-releases: 2.0.19 - update-browserslist-db: 1.1.2(browserslist@4.24.4) - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 + update-browserslist-db: 1.1.3(browserslist@4.24.5) buffer-from@1.1.2: {} @@ -11212,12 +8676,11 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.7: + call-bind@1.0.8: dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 call-bound@1.0.4: @@ -11241,7 +8704,7 @@ snapshots: camelize@1.0.1: {} - caniuse-lite@1.0.30001692: {} + caniuse-lite@1.0.30001718: {} chai@5.2.0: dependencies: @@ -11251,14 +8714,6 @@ snapshots: loupe: 3.1.3 pathval: 2.0.0 - chalk@1.1.3: - dependencies: - ansi-styles: 2.2.1 - escape-string-regexp: 1.0.5 - has-ansi: 2.0.0 - strip-ansi: 3.0.1 - supports-color: 2.0.0 - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -11272,15 +8727,11 @@ snapshots: chalk@5.4.1: {} - char-regex@1.0.2: {} - chardet@0.7.0: {} check-error@2.1.1: {} - ci-info@3.9.0: {} - - cjs-module-lexer@1.4.1: {} + cjs-module-lexer@1.4.3: {} classnames@2.5.1: {} @@ -11296,8 +8747,6 @@ snapshots: dependencies: restore-cursor: 5.1.0 - cli-spinners@2.6.1: {} - cli-spinners@2.9.2: {} cli-truncate@4.0.0: @@ -11327,12 +8776,8 @@ snapshots: clone@1.0.4: {} - co@4.6.0: {} - code-block-writer@13.0.3: {} - collect-v8-coverage@1.0.2: {} - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -11349,11 +8794,6 @@ snapshots: colorette@2.0.20: {} - columnify@1.6.0: - dependencies: - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -11368,8 +8808,6 @@ snapshots: commander@4.1.1: {} - common-tags@1.8.2: {} - commondir@1.0.1: {} compare-func@2.0.0: @@ -11386,6 +8824,10 @@ snapshots: readable-stream: 3.6.2 typedarray: 0.0.6 + config@4.0.0: + dependencies: + json5: 2.2.3 + conventional-changelog-angular@5.0.13: dependencies: compare-func: 2.0.0 @@ -11517,16 +8959,14 @@ snapshots: convert-source-map@2.0.0: {} - cookie@0.7.2: {} + cookie@1.0.2: {} - core-js-compat@3.40.0: + core-js-compat@3.42.0: dependencies: - browserslist: 4.24.4 + browserslist: 4.24.5 core-util-is@1.0.3: {} - corser@2.0.1: {} - cosmiconfig-typescript-loader@6.1.0(@types/node@22.15.18)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3): dependencies: '@types/node': 22.15.18 @@ -11537,14 +8977,14 @@ snapshots: cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 - import-fresh: 3.3.0 + import-fresh: 3.3.1 parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 cosmiconfig@8.3.6(typescript@5.8.3): dependencies: - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 @@ -11554,7 +8994,7 @@ snapshots: cosmiconfig@9.0.0(typescript@5.8.3): dependencies: env-paths: 2.2.1 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 optionalDependencies: @@ -11570,7 +9010,7 @@ snapshots: transitivePeerDependencies: - encoding - cross-spawn@6.0.5: + cross-spawn@6.0.6: dependencies: nice-try: 1.0.5 path-key: 2.0.1 @@ -11578,12 +9018,6 @@ snapshots: shebang-command: 1.2.0 which: 1.3.1 - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -11614,20 +9048,12 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 dateformat@3.0.3: {} dateformat@4.6.3: {} - debug@3.2.7: - dependencies: - ms: 2.1.3 - - debug@4.3.1: - dependencies: - ms: 2.1.2 - debug@4.3.4: dependencies: ms: 2.1.2 @@ -11649,34 +9075,28 @@ snapshots: decamelize@1.2.0: {} - dedent@1.5.3(babel-plugin-macros@3.1.0): - optionalDependencies: - babel-plugin-macros: 3.1.0 - deep-eql@5.0.2: {} deep-equal@2.2.3: dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 es-get-iterator: 1.1.3 - get-intrinsic: 1.2.4 - is-arguments: 1.1.1 - is-array-buffer: 3.0.4 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 + get-intrinsic: 1.3.0 + is-arguments: 1.2.0 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 isarray: 2.0.5 object-is: 1.1.6 object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.3 - side-channel: 1.0.6 - which-boxed-primitive: 1.0.2 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 + side-channel: 1.1.0 + which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.15 - - deep-is@0.1.4: {} + which-typed-array: 1.1.19 deepmerge@4.3.1: {} @@ -11686,11 +9106,9 @@ snapshots: define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.0.1 - - define-lazy-prop@2.0.0: {} + gopd: 1.2.0 define-properties@1.2.1: dependencies: @@ -11706,7 +9124,7 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.3: {} + detect-libc@2.0.4: {} detect-newline@3.1.0: {} @@ -11717,35 +9135,21 @@ snapshots: transitivePeerDependencies: - supports-color - diff-sequences@29.6.3: {} - diff@4.0.2: {} dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dlv@1.1.3: {} - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 csstype: 3.1.3 dot-prop@5.3.0: dependencies: is-obj: 2.0.0 - dotenv-expand@11.0.7: - dependencies: - dotenv: 16.5.0 - - dotenv@16.4.7: {} - dotenv@16.5.0: {} dotgitignore@2.1.0: @@ -11763,20 +9167,14 @@ snapshots: eastasianwidth@0.2.0: {} - eciesjs@0.4.11: + eciesjs@0.4.14: dependencies: - '@ecies/ciphers': 0.2.1(@noble/ciphers@1.0.0) - '@noble/ciphers': 1.0.0 - '@noble/curves': 1.6.0 - '@noble/hashes': 1.5.0 - - ejs@3.1.10: - dependencies: - jake: 10.9.2 - - electron-to-chromium@1.5.80: {} + '@ecies/ciphers': 0.2.3(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.0 + '@noble/hashes': 1.8.0 - emittery@0.13.1: {} + electron-to-chromium@1.5.152: {} emoji-regex@10.4.0: {} @@ -11788,10 +9186,6 @@ snapshots: dependencies: once: 1.4.0 - enquirer@2.3.6: - dependencies: - ansi-colors: 4.1.3 - entities@4.5.0: {} env-paths@2.2.1: {} @@ -11802,25 +9196,21 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - es-define-property@1.0.1: {} es-errors@1.3.0: {} es-get-iterator@1.1.3: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - is-arguments: 1.1.1 + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + is-arguments: 1.2.0 is-map: 2.0.3 is-set: 2.0.3 - is-string: 1.0.7 + is-string: 1.1.1 isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 + stop-iteration-iterator: 1.1.0 es-module-lexer@1.7.0: {} @@ -11828,208 +9218,51 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - - esbuild@0.25.0: + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.25.4: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.0 - '@esbuild/android-arm': 0.25.0 - '@esbuild/android-arm64': 0.25.0 - '@esbuild/android-x64': 0.25.0 - '@esbuild/darwin-arm64': 0.25.0 - '@esbuild/darwin-x64': 0.25.0 - '@esbuild/freebsd-arm64': 0.25.0 - '@esbuild/freebsd-x64': 0.25.0 - '@esbuild/linux-arm': 0.25.0 - '@esbuild/linux-arm64': 0.25.0 - '@esbuild/linux-ia32': 0.25.0 - '@esbuild/linux-loong64': 0.25.0 - '@esbuild/linux-mips64el': 0.25.0 - '@esbuild/linux-ppc64': 0.25.0 - '@esbuild/linux-riscv64': 0.25.0 - '@esbuild/linux-s390x': 0.25.0 - '@esbuild/linux-x64': 0.25.0 - '@esbuild/netbsd-arm64': 0.25.0 - '@esbuild/netbsd-x64': 0.25.0 - '@esbuild/openbsd-arm64': 0.25.0 - '@esbuild/openbsd-x64': 0.25.0 - '@esbuild/sunos-x64': 0.25.0 - '@esbuild/win32-arm64': 0.25.0 - '@esbuild/win32-ia32': 0.25.0 - '@esbuild/win32-x64': 0.25.0 + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 escalade@3.2.0: {} escape-string-regexp@1.0.5: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.5(eslint@9.27.0(jiti@2.4.2)): - dependencies: - eslint: 9.27.0(jiti@2.4.2) - - eslint-plugin-prettier@5.4.0(eslint-config-prettier@10.1.5(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(prettier@3.5.3): - dependencies: - eslint: 9.27.0(jiti@2.4.2) - prettier: 3.5.3 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.1 - optionalDependencies: - eslint-config-prettier: 10.1.5(eslint@9.27.0(jiti@2.4.2)) - - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-scope@8.3.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.0: {} - - eslint@8.57.1: - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.1 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.1(supports-color@5.5.0) - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - eslint@9.27.0(jiti@2.4.2): - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.1 - '@eslint/core': 0.14.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.27.0 - '@eslint/plugin-kit': 0.3.1 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.2 - '@types/estree': 1.0.7 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.1(supports-color@5.5.0) - escape-string-regexp: 4.0.0 - eslint-scope: 8.3.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.4.2 - transitivePeerDependencies: - - supports-color - esm@3.2.25: {} - espree@10.3.0: - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 - - espree@9.6.1: - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 3.4.3 - esprima@4.0.1: {} - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -12038,8 +9271,6 @@ snapshots: esutils@2.0.3: {} - eventemitter3@4.0.7: {} - eventemitter3@5.0.1: {} execa@5.1.1: @@ -12054,18 +9285,8 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - exit@0.1.2: {} - expect-type@1.2.1: {} - expect@29.7.0: - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - external-editor@3.1.0: dependencies: chardet: 0.7.0 @@ -12078,9 +9299,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -12088,20 +9307,15 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} - - fast-json-stringify@6.0.0: + fast-json-stringify@6.0.1: dependencies: - '@fastify/merge-json-schemas': 0.1.1 + '@fastify/merge-json-schemas': 0.2.1 ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) - fast-deep-equal: 3.1.3 - fast-uri: 2.4.0 - json-schema-ref-resolver: 1.0.1 + fast-uri: 3.0.6 + json-schema-ref-resolver: 2.0.1 rfdc: 1.4.1 - fast-levenshtein@2.0.6: {} - fast-querystring@1.1.2: dependencies: fast-decode-uri-component: 1.0.1 @@ -12110,38 +9324,32 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-uri@2.4.0: {} - fast-uri@3.0.6: {} fastify-plugin@5.0.1: {} fastify@5.3.3: dependencies: - '@fastify/ajv-compiler': 4.0.1 - '@fastify/error': 4.0.0 - '@fastify/fast-json-stringify-compiler': 5.0.1 + '@fastify/ajv-compiler': 4.0.2 + '@fastify/error': 4.1.0 + '@fastify/fast-json-stringify-compiler': 5.0.3 '@fastify/proxy-addr': 5.0.0 abstract-logging: 2.0.1 - avvio: 9.0.0 - fast-json-stringify: 6.0.0 - find-my-way: 9.1.0 - light-my-request: 6.1.0 + avvio: 9.1.0 + fast-json-stringify: 6.0.1 + find-my-way: 9.3.0 + light-my-request: 6.6.0 pino: 9.6.0 process-warning: 5.0.0 rfdc: 1.4.1 secure-json-parse: 4.0.0 - semver: 7.7.1 + semver: 7.7.2 toad-cache: 3.7.0 fastq@1.19.1: dependencies: reusify: 1.1.0 - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - fdir@6.4.4(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -12152,18 +9360,6 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -12174,11 +9370,11 @@ snapshots: make-dir: 2.1.0 pkg-dir: 3.0.0 - find-my-way@9.1.0: + find-my-way@9.3.0: dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 - safe-regex2: 4.0.0 + safe-regex2: 5.0.0 find-root@1.1.0: {} @@ -12206,17 +9402,6 @@ snapshots: path-exists: 5.0.0 unicorn-magic: 0.1.0 - flat-cache@3.2.0: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - flat@5.0.2: {} flatted@3.3.3: {} @@ -12225,31 +9410,26 @@ snapshots: optionalDependencies: debug: 4.4.1(supports-color@5.5.0) - for-each@0.3.3: + for-each@0.3.5: dependencies: is-callable: 1.2.7 - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.1: + form-data@4.0.2: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 mime-types: 2.1.35 forwarded-parse@2.1.2: {} forwarded@0.2.0: {} - front-matter@4.0.2: - dependencies: - js-yaml: 3.14.1 - - fs-constants@1.0.0: {} - fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -12271,15 +9451,7 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.2.0: {} - - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 + get-east-asian-width@1.3.0: {} get-intrinsic@1.3.0: dependencies: @@ -12310,9 +9482,7 @@ snapshots: get-stream@6.0.1: {} - get-them-args@1.3.2: {} - - get-tsconfig@4.8.1: + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -12350,37 +9520,24 @@ snapshots: dependencies: is-glob: 4.0.3 - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@11.0.0: + glob@11.0.2: dependencies: - foreground-child: 3.3.0 - jackspeak: 4.0.2 + foreground-child: 3.3.1 + jackspeak: 4.1.0 minimatch: 10.0.1 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 2.0.0 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - glob@8.1.0: dependencies: fs.realpath: 1.0.0 @@ -12395,33 +9552,21 @@ snapshots: globals@11.12.0: {} - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globals@14.0.0: {} - globals@16.1.0: {} globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 - gopd@1.2.0: {} graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - gud@1.0.0: {} handlebars@4.7.8: @@ -12435,13 +9580,7 @@ snapshots: hard-rejection@2.1.0: {} - harmony-reflect@1.6.2: {} - - has-ansi@2.0.0: - dependencies: - ansi-regex: 2.1.1 - - has-bigints@1.0.2: {} + has-bigints@1.1.0: {} has-flag@3.0.0: {} @@ -12449,24 +9588,18 @@ snapshots: has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} + es-define-property: 1.0.1 has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 hasown@2.0.2: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - help-me@5.0.0: {} hoist-non-react-statics@3.3.2: @@ -12479,14 +9612,6 @@ snapshots: dependencies: lru-cache: 6.0.0 - hosted-git-info@7.0.2: - dependencies: - lru-cache: 10.4.3 - - html-encoding-sniffer@3.0.0: - dependencies: - whatwg-encoding: 2.0.0 - html-escaper@2.0.2: {} html-parse-stringify@3.0.1: @@ -12501,33 +9626,6 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 - http-proxy@1.18.1(debug@4.4.1): - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.9(debug@4.4.1) - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - - http-server@14.1.1(debug@4.4.1): - dependencies: - basic-auth: 2.0.1 - chalk: 4.1.2 - corser: 2.0.1 - he: 1.2.0 - html-encoding-sniffer: 3.0.0 - http-proxy: 1.18.1(debug@4.4.1) - mime: 1.6.0 - minimist: 1.2.8 - opener: 1.5.2 - portfinder: 1.0.32 - secure-compare: 3.0.1 - union: 0.5.0 - url-join: 4.0.1 - transitivePeerDependencies: - - debug - - supports-color - https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 @@ -12541,9 +9639,9 @@ snapshots: i18next-browser-languagedetector@7.2.2: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 - i18next-http-backend@2.7.1: + i18next-http-backend@2.7.3: dependencies: cross-fetch: 4.0.0 transitivePeerDependencies: @@ -12551,44 +9649,32 @@ snapshots: i18next@22.5.1: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - identity-obj-proxy@3.0.0: - dependencies: - harmony-reflect: 1.6.2 - ieee754@1.2.1: {} ignore@5.3.2: {} - ignore@7.0.4: {} - immutable@4.3.7: {} - import-fresh@3.3.0: + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 import-in-the-middle@1.13.2: dependencies: - acorn: 8.14.0 - acorn-import-attributes: 1.9.5(acorn@8.14.0) - cjs-module-lexer: 1.4.1 - module-details-from-path: 1.0.3 + acorn: 8.14.1 + acorn-import-attributes: 1.9.5(acorn@8.14.1) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 import-meta-resolve@4.1.0: {} - imurmurhash@0.1.4: {} - indent-string@4.0.0: {} inflection@1.13.4: {} @@ -12606,20 +9692,20 @@ snapshots: inquirer@9.3.7: dependencies: - '@inquirer/figures': 1.0.7 + '@inquirer/figures': 1.0.11 ansi-escapes: 4.3.2 cli-width: 4.1.0 external-editor: 3.1.0 mute-stream: 1.0.0 ora: 5.4.1 run-async: 3.0.0 - rxjs: 7.8.1 + rxjs: 7.8.2 string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 - internal-slot@1.0.7: + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 @@ -12631,43 +9717,41 @@ snapshots: dependencies: inquirer: 9.3.7 - ip-regex@4.3.0: {} - ipaddr.js@2.2.0: {} - is-arguments@1.1.1: + is-arguments@1.2.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-array-buffer@3.0.4: + is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 is-arrayish@0.2.1: {} - is-bigint@1.0.4: + is-bigint@1.1.0: dependencies: - has-bigints: 1.0.2 + has-bigints: 1.1.0 - is-boolean-object@1.1.2: + is-boolean-object@1.2.2: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-callable@1.2.7: {} - is-core-module@2.15.1: + is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-date-object@1.0.5: + is-date-object@1.1.0: dependencies: + call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-docker@2.2.1: {} - is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -12676,9 +9760,7 @@ snapshots: is-fullwidth-code-point@5.0.0: dependencies: - get-east-asian-width: 1.2.0 - - is-generator-fn@2.1.0: {} + get-east-asian-width: 1.3.0 is-glob@4.0.3: dependencies: @@ -12692,16 +9774,15 @@ snapshots: is-module@1.0.0: {} - is-number-object@1.0.7: + is-number-object@1.1.1: dependencies: + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-number@7.0.0: {} is-obj@2.0.0: {} - is-path-inside@3.0.3: {} - is-plain-obj@1.1.0: {} is-plain-object@2.0.4: @@ -12714,26 +9795,31 @@ snapshots: dependencies: '@types/estree': 1.0.7 - is-regex@1.1.4: + is-regex@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.4 + gopd: 1.2.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 is-set@2.0.3: {} - is-shared-array-buffer@1.0.3: + is-shared-array-buffer@1.0.4: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.4 is-stream@2.0.1: {} - is-string@1.0.7: + is-string@1.1.1: dependencies: + call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-symbol@1.0.4: + is-symbol@1.1.1: dependencies: - has-symbols: 1.0.3 + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 is-text-path@1.0.1: dependencies: @@ -12747,24 +9833,12 @@ snapshots: is-unicode-supported@1.3.0: {} - is-url@1.2.4: {} - is-weakmap@2.0.2: {} - is-weakset@2.0.3: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-wsl@2.2.0: + is-weakset@2.0.4: dependencies: - is-docker: 2.2.1 - - is2@2.0.9: - dependencies: - deep-is: 0.1.4 - ip-regex: 4.3.0 - is-url: 1.2.4 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 isarray@1.0.0: {} @@ -12778,334 +9852,34 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.26.0 - '@babel/parser': 7.26.5 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.26.0 - '@babel/parser': 7.26.5 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.7.1 - transitivePeerDependencies: - - supports-color - istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: - dependencies: - debug: 4.4.1(supports-color@5.5.0) - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.1(supports-color@5.5.0) - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jackspeak@4.0.2: - dependencies: - '@isaacs/cliui': 8.0.2 - - jake@10.9.2: - dependencies: - async: 3.2.6 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - - jest-circus@29.7.0(babel-plugin-macros@3.1.0): - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.3(babel-plugin-macros@3.1.0) - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3)): - dependencies: - '@babel/core': 7.26.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0(babel-plugin-macros@3.1.0) - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.15.18 - ts-node: 10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-docblock@29.7.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - - jest-environment-node@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.18 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-matcher-utils@29.7.0: - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-message-util@29.7.0: - dependencies: - '@babel/code-frame': 7.26.2 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - jest-util: 29.7.0 - - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 - - jest-regex-util@29.6.3: {} - - jest-resolve@29.7.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.8 - resolve.exports: 2.0.3 - slash: 3.0.0 - - jest-runner@29.7.0: - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - jest-runtime@29.7.0: + istanbul-lib-source-maps@5.0.6: dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - chalk: 4.1.2 - cjs-module-lexer: 1.4.1 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.26.0 - '@babel/generator': 7.26.5 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) - '@babel/types': 7.26.5 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.7.1 + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.4.1(supports-color@5.5.0) + istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color - jest-util@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - jest-validate@29.7.0: + istanbul-reports@3.1.7: dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 - jest-watcher@29.7.0: + jackspeak@3.4.3: dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.18 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 - jest-worker@29.7.0: + jackspeak@4.1.0: dependencies: - '@types/node': 22.15.18 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 + '@isaacs/cliui': 8.0.2 jiti@2.4.2: {} @@ -13113,11 +9887,6 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -13126,28 +9895,20 @@ snapshots: jsesc@3.1.0: {} - json-buffer@3.0.1: {} - json-parse-better-errors@1.0.2: {} json-parse-even-better-errors@2.3.1: {} - json-schema-ref-resolver@1.0.1: + json-schema-ref-resolver@2.0.1: dependencies: - fast-deep-equal: 3.1.3 - - json-schema-traverse@0.4.1: {} + dequal: 2.0.3 json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - json-stringify-safe@5.0.1: {} json5@2.2.3: {} - jsonc-parser@3.2.0: {} - jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -13158,15 +9919,6 @@ snapshots: jw-paginate@1.0.4: {} - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kill-port@1.6.1: - dependencies: - get-them-args: 1.3.2 - shell-exec: 1.0.2 - kind-of@6.0.3: {} knex@3.1.0(pg@8.15.6): @@ -13190,30 +9942,21 @@ snapshots: transitivePeerDependencies: - supports-color - leven@3.1.0: {} - - levn@0.4.1: + light-my-request@6.6.0: dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - light-my-request@6.1.0: - dependencies: - cookie: 0.7.2 + cookie: 1.0.2 process-warning: 4.0.1 - set-cookie-parser: 2.7.0 + set-cookie-parser: 2.7.1 lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} - lines-and-columns@2.0.3: {} - linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 - linkifyjs@4.2.0: {} + linkifyjs@4.3.1: {} lint-staged@16.0.0: dependencies: @@ -13316,13 +10059,6 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - loglevel-colored-level-prefix@1.0.0: - dependencies: - chalk: 1.1.3 - loglevel: 1.9.2 - - loglevel@1.9.2: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -13331,7 +10067,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.0.1: {} + lru-cache@11.1.0: {} lru-cache@5.1.1: dependencies: @@ -13347,8 +10083,8 @@ snapshots: magicast@0.3.5: dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 source-map-js: 1.2.1 make-dir@2.1.0: @@ -13358,14 +10094,10 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.1 + semver: 7.7.2 make-error@1.3.6: {} - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - map-obj@1.0.1: {} map-obj@4.3.0: {} @@ -13420,8 +10152,6 @@ snapshots: dependencies: mime-db: 1.52.0 - mime@1.6.0: {} - mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -13440,10 +10170,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -13458,19 +10184,15 @@ snapshots: minipass@7.1.2: {} - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mkdirp@1.0.4: {} modern-spawn@1.0.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 modify-values@1.0.1: {} - module-details-from-path@1.0.3: {} + module-details-from-path@1.0.4: {} moment-timezone@0.5.46: dependencies: @@ -13478,7 +10200,7 @@ snapshots: moment@2.30.1: {} - mrmime@2.0.0: {} + mrmime@2.0.1: {} ms@2.1.2: {} @@ -13494,9 +10216,7 @@ snapshots: nano-spawn@1.0.1: {} - nanoid@3.3.8: {} - - natural-compare@1.4.0: {} + nanoid@3.3.11: {} neo-async@2.6.2: {} @@ -13504,7 +10224,7 @@ snapshots: node-abi@3.75.0: dependencies: - semver: 7.7.1 + semver: 7.7.2 node-addon-api@8.3.1: {} @@ -13514,112 +10234,46 @@ snapshots: node-gyp-build@4.8.4: {} - node-int64@0.4.0: {} - - node-machine-id@1.1.12: {} - node-releases@2.0.19: {} normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.8 + resolve: 1.22.10 semver: 5.7.2 validate-npm-package-license: 3.0.4 normalize-package-data@3.0.3: dependencies: hosted-git-info: 4.1.0 - is-core-module: 2.15.1 - semver: 7.7.1 + is-core-module: 2.16.1 + semver: 7.7.2 validate-npm-package-license: 3.0.4 - normalize-path@3.0.0: {} - - npm-package-arg@11.0.1: - dependencies: - hosted-git-info: 7.0.2 - proc-log: 3.0.0 - semver: 7.7.1 - validate-npm-package-name: 5.0.1 - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 - nx@21.0.3(@swc-node/register@1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3))(@swc/core@1.11.22(@swc/helpers@0.5.17))(debug@4.4.1): - dependencies: - '@napi-rs/wasm-runtime': 0.2.4 - '@yarnpkg/lockfile': 1.1.0 - '@yarnpkg/parsers': 3.0.2 - '@zkochan/js-yaml': 0.0.7 - axios: 1.8.3(debug@4.4.1) - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.6.1 - cliui: 8.0.1 - dotenv: 16.4.7 - dotenv-expand: 11.0.7 - enquirer: 2.3.6 - figures: 3.2.0 - flat: 5.0.2 - front-matter: 4.0.2 - ignore: 5.3.2 - jest-diff: 29.7.0 - jsonc-parser: 3.2.0 - lines-and-columns: 2.0.3 - minimatch: 9.0.3 - node-machine-id: 1.1.12 - npm-run-path: 4.0.1 - open: 8.4.2 - ora: 5.3.0 - resolve.exports: 2.0.3 - semver: 7.7.1 - string-width: 4.2.3 - tar-stream: 2.2.0 - tmp: 0.2.3 - tree-kill: 1.2.2 - tsconfig-paths: 4.2.0 - tslib: 2.8.1 - yaml: 2.7.0 - yargs: 17.7.2 - yargs-parser: 21.1.1 - optionalDependencies: - '@nx/nx-darwin-arm64': 21.0.3 - '@nx/nx-darwin-x64': 21.0.3 - '@nx/nx-freebsd-x64': 21.0.3 - '@nx/nx-linux-arm-gnueabihf': 21.0.3 - '@nx/nx-linux-arm64-gnu': 21.0.3 - '@nx/nx-linux-arm64-musl': 21.0.3 - '@nx/nx-linux-x64-gnu': 21.0.3 - '@nx/nx-linux-x64-musl': 21.0.3 - '@nx/nx-win32-arm64-msvc': 21.0.3 - '@nx/nx-win32-x64-msvc': 21.0.3 - '@swc-node/register': 1.10.10(@swc/core@1.11.22(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3) - '@swc/core': 1.11.22(@swc/helpers@0.5.17) - transitivePeerDependencies: - - debug - object-assign@4.1.1: {} - object-inspect@1.13.2: {} - object-inspect@1.13.4: {} object-is@1.1.6: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 object-keys@1.1.1: {} object-treeify@1.1.33: {} - object.assign@4.1.5: + object.assign@4.1.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - has-symbols: 1.0.3 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 object-keys: 1.1.1 obuf@1.1.2: {} @@ -13638,34 +10292,6 @@ snapshots: dependencies: mimic-function: 5.0.1 - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - - opener@1.5.2: {} - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - ora@5.3.0: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - ora@5.4.1: dependencies: bl: 4.1.0 @@ -13694,19 +10320,21 @@ snapshots: os-tmpdir@1.0.2: {} - oxc-resolver@5.0.0: + oxc-resolver@5.3.0: optionalDependencies: - '@oxc-resolver/binding-darwin-arm64': 5.0.0 - '@oxc-resolver/binding-darwin-x64': 5.0.0 - '@oxc-resolver/binding-freebsd-x64': 5.0.0 - '@oxc-resolver/binding-linux-arm-gnueabihf': 5.0.0 - '@oxc-resolver/binding-linux-arm64-gnu': 5.0.0 - '@oxc-resolver/binding-linux-arm64-musl': 5.0.0 - '@oxc-resolver/binding-linux-x64-gnu': 5.0.0 - '@oxc-resolver/binding-linux-x64-musl': 5.0.0 - '@oxc-resolver/binding-wasm32-wasi': 5.0.0 - '@oxc-resolver/binding-win32-arm64-msvc': 5.0.0 - '@oxc-resolver/binding-win32-x64-msvc': 5.0.0 + '@oxc-resolver/binding-darwin-arm64': 5.3.0 + '@oxc-resolver/binding-darwin-x64': 5.3.0 + '@oxc-resolver/binding-freebsd-x64': 5.3.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 5.3.0 + '@oxc-resolver/binding-linux-arm64-gnu': 5.3.0 + '@oxc-resolver/binding-linux-arm64-musl': 5.3.0 + '@oxc-resolver/binding-linux-riscv64-gnu': 5.3.0 + '@oxc-resolver/binding-linux-s390x-gnu': 5.3.0 + '@oxc-resolver/binding-linux-x64-gnu': 5.3.0 + '@oxc-resolver/binding-linux-x64-musl': 5.3.0 + '@oxc-resolver/binding-wasm32-wasi': 5.3.0 + '@oxc-resolver/binding-win32-arm64-msvc': 5.3.0 + '@oxc-resolver/binding-win32-x64-msvc': 5.3.0 p-limit@1.3.0: dependencies: @@ -13722,7 +10350,7 @@ snapshots: p-limit@4.0.0: dependencies: - yocto-queue: 1.1.1 + yocto-queue: 1.2.1 p-locate@2.0.0: dependencies: @@ -13763,7 +10391,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -13776,8 +10404,6 @@ snapshots: path-exists@5.0.0: {} - path-is-absolute@1.0.1: {} - path-key@2.0.1: {} path-key@3.1.1: {} @@ -13791,7 +10417,7 @@ snapshots: path-scurry@2.0.0: dependencies: - lru-cache: 11.0.1 + lru-cache: 11.1.0 minipass: 7.1.2 path-type@3.0.0: @@ -13811,11 +10437,9 @@ snapshots: pg-connection-string@2.7.0: {} - pg-connection-string@2.8.5: {} - pg-connection-string@2.9.0: {} - pg-cursor@2.12.0(pg@8.16.0): + pg-cursor@2.15.0(pg@8.16.0): dependencies: pg: 8.16.0 @@ -13827,13 +10451,13 @@ snapshots: pg-numeric@1.0.2: {} - pg-pool@3.10.0(pg@8.16.0): + pg-pool@3.10.0(pg@8.15.6): dependencies: - pg: 8.16.0 + pg: 8.15.6 - pg-pool@3.9.6(pg@8.15.6): + pg-pool@3.10.0(pg@8.16.0): dependencies: - pg: 8.15.6 + pg: 8.16.0 pg-protocol@1.10.0: {} @@ -13857,8 +10481,8 @@ snapshots: pg@8.15.6: dependencies: - pg-connection-string: 2.8.5 - pg-pool: 3.9.6(pg@8.15.6) + pg-connection-string: 2.9.0 + pg-pool: 3.10.0(pg@8.15.6) pg-protocol: 1.10.0 pg-types: 2.2.0 pgpass: 1.0.5 @@ -13934,7 +10558,7 @@ snapshots: sonic-boom: 4.1.0 thread-stream: 3.1.0 - pirates@4.0.6: {} + pirates@4.0.7: {} pkg-dir@3.0.0: dependencies: @@ -13944,23 +10568,15 @@ snapshots: polished@4.3.1: dependencies: - '@babel/runtime': 7.26.0 - - portfinder@1.0.32: - dependencies: - async: 2.6.4 - debug: 3.2.7 - mkdirp: 0.5.6 - transitivePeerDependencies: - - supports-color + '@babel/runtime': 7.27.1 - possible-typed-array-names@1.0.0: {} + possible-typed-array-names@1.1.0: {} postcss-value-parser@4.2.0: {} postcss@8.5.3: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -13988,40 +10604,6 @@ snapshots: postgres-range@1.1.4: {} - prelude-ls@1.2.1: {} - - prettier-eslint@16.4.2(typescript@5.8.3): - dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.3) - common-tags: 1.8.2 - dlv: 1.1.3 - eslint: 8.57.1 - indent-string: 4.0.0 - lodash.merge: 4.6.2 - loglevel-colored-level-prefix: 1.0.0 - prettier: 3.5.3 - pretty-format: 29.7.0 - require-relative: 0.8.7 - tslib: 2.8.1 - vue-eslint-parser: 9.4.3(eslint@8.57.1) - transitivePeerDependencies: - - supports-color - - typescript - - prettier-linter-helpers@1.0.0: - dependencies: - fast-diff: 1.3.0 - - prettier@3.5.3: {} - - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - - proc-log@3.0.0: {} - process-nextick-args@2.0.1: {} process-warning@4.0.0: {} @@ -14038,108 +10620,108 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - prosemirror-changeset@2.2.1: + prosemirror-changeset@2.3.0: dependencies: - prosemirror-transform: 1.10.2 + prosemirror-transform: 1.10.4 prosemirror-collab@1.3.1: dependencies: prosemirror-state: 1.4.3 - prosemirror-commands@1.6.1: + prosemirror-commands@1.7.1: dependencies: - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 + prosemirror-transform: 1.10.4 - prosemirror-dropcursor@1.8.1: + prosemirror-dropcursor@1.8.2: dependencies: prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 - prosemirror-view: 1.34.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.39.2 prosemirror-gapcursor@1.3.2: dependencies: - prosemirror-keymap: 1.2.2 - prosemirror-model: 1.23.0 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 - prosemirror-view: 1.34.3 + prosemirror-view: 1.39.2 prosemirror-history@1.4.1: dependencies: prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 - prosemirror-view: 1.34.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.39.2 rope-sequence: 1.3.4 - prosemirror-inputrules@1.4.0: + prosemirror-inputrules@1.5.0: dependencies: prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 + prosemirror-transform: 1.10.4 - prosemirror-keymap@1.2.2: + prosemirror-keymap@1.2.3: dependencies: prosemirror-state: 1.4.3 w3c-keyname: 2.2.8 - prosemirror-markdown@1.13.1: + prosemirror-markdown@1.13.2: dependencies: '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 - prosemirror-menu@1.2.4: + prosemirror-menu@1.2.5: dependencies: crelt: 1.0.6 - prosemirror-commands: 1.6.1 + prosemirror-commands: 1.7.1 prosemirror-history: 1.4.1 prosemirror-state: 1.4.3 - prosemirror-model@1.23.0: + prosemirror-model@1.25.1: dependencies: orderedmap: 2.1.1 - prosemirror-schema-basic@1.2.3: + prosemirror-schema-basic@1.2.4: dependencies: - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 - prosemirror-schema-list@1.4.1: + prosemirror-schema-list@1.5.1: dependencies: - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 + prosemirror-transform: 1.10.4 prosemirror-state@1.4.3: dependencies: - prosemirror-model: 1.23.0 - prosemirror-transform: 1.10.2 - prosemirror-view: 1.34.3 + prosemirror-model: 1.25.1 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.39.2 - prosemirror-tables@1.5.0: + prosemirror-tables@1.7.1: dependencies: - prosemirror-keymap: 1.2.2 - prosemirror-model: 1.23.0 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 - prosemirror-view: 1.34.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.39.2 - prosemirror-trailing-node@2.0.9(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3): + prosemirror-trailing-node@2.0.9(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.2): dependencies: '@remirror/core-constants': 2.0.2 escape-string-regexp: 4.0.0 - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 - prosemirror-view: 1.34.3 + prosemirror-view: 1.39.2 - prosemirror-transform@1.10.2: + prosemirror-transform@1.10.4: dependencies: - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 - prosemirror-view@1.34.3: + prosemirror-view@1.39.2: dependencies: - prosemirror-model: 1.23.0 + prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.2 + prosemirror-transform: 1.10.4 proxy-from-env@1.1.0: {} @@ -14152,14 +10734,8 @@ snapshots: punycode@2.3.1: {} - pure-rand@6.1.0: {} - q@1.5.1: {} - qs@6.13.1: - dependencies: - side-channel: 1.0.6 - qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -14172,7 +10748,7 @@ snapshots: raf-schd@4.0.3: {} - react-currency-input-field@3.9.0(react@18.3.1): + react-currency-input-field@3.10.0(react@18.3.1): dependencies: react: 18.3.1 @@ -14184,7 +10760,7 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-onclickoutside: 6.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-onclickoutside: 6.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom@18.3.1(react@18.3.1): @@ -14202,7 +10778,7 @@ snapshots: react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 html-parse-stringify: 3.0.1 i18next: 22.5.1 react: 18.3.1 @@ -14213,7 +10789,7 @@ snapshots: react-is@18.3.1: {} - react-onclickoutside@6.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-onclickoutside@6.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -14237,45 +10813,45 @@ snapshots: react-fast-compare: 3.2.2 warning: 4.0.3 - react-redux@8.1.3(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1): + react-redux@8.1.3(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 '@types/hoist-non-react-statics': 3.3.6 '@types/use-sync-external-store': 0.0.3 hoist-non-react-statics: 3.3.2 react: 18.3.1 react-is: 18.3.1 - use-sync-external-store: 1.4.0(react@18.3.1) + use-sync-external-store: 1.5.0(react@18.3.1) optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.1.4 react-dom: 18.3.1(react@18.3.1) redux: 4.2.1 - react-router-dom@6.28.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router-dom@6.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@remix-run/router': 1.21.0 + '@remix-run/router': 1.23.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-router: 6.28.1(react@18.3.1) + react-router: 6.30.0(react@18.3.1) - react-router@6.28.1(react@18.3.1): + react-router@6.30.0(react@18.3.1): dependencies: - '@remix-run/router': 1.21.0 + '@remix-run/router': 1.23.0 react: 18.3.1 - react-select@5.9.0(@types/react@19.0.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-select@5.10.1(@types/react@19.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 '@emotion/cache': 11.14.0 - '@emotion/react': 11.14.0(@types/react@19.0.4)(react@18.3.1) - '@floating-ui/dom': 1.6.13 - '@types/react-transition-group': 4.4.12(@types/react@19.0.4) + '@emotion/react': 11.14.0(@types/react@19.1.4)(react@18.3.1) + '@floating-ui/dom': 1.7.0 + '@types/react-transition-group': 4.4.12(@types/react@19.1.4) memoize-one: 6.0.0 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - use-isomorphic-layout-effect: 1.2.0(@types/react@19.0.4)(react@18.3.1) + use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.4)(react@18.3.1) transitivePeerDependencies: - '@types/react' - supports-color @@ -14287,7 +10863,7 @@ snapshots: react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -14342,7 +10918,7 @@ snapshots: rechoir@0.8.0: dependencies: - resolve: 1.22.8 + resolve: 1.22.10 redent@3.0.0: dependencies: @@ -14351,7 +10927,7 @@ snapshots: redux@4.2.1: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.27.1 reflect-metadata@0.2.2: {} @@ -14363,15 +10939,13 @@ snapshots: regenerator-runtime@0.14.1: {} - regenerator-transform@0.15.2: - dependencies: - '@babel/runtime': 7.26.0 - - regexp.prototype.flags@1.5.3: + regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 set-function-name: 2.0.2 regexpu-core@6.2.0: @@ -14393,29 +10967,23 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@7.4.0: + require-in-the-middle@7.5.2: dependencies: debug: 4.4.1(supports-color@5.5.0) - module-details-from-path: 1.0.3 - resolve: 1.22.8 + module-details-from-path: 1.0.4 + resolve: 1.22.10 transitivePeerDependencies: - supports-color - require-relative@0.8.7: {} - - requires-port@1.0.0: {} - resolve-from@4.0.0: {} resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.3: {} - - resolve@1.22.8: + resolve@1.22.10: dependencies: - is-core-module: 2.15.1 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -14442,71 +11010,45 @@ snapshots: rfdc@1.4.1: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rimraf@6.0.1: dependencies: - glob: 11.0.0 + glob: 11.0.2 package-json-from-dist: 1.0.1 - rollup-plugin-esbuild-minify@1.2.0(rollup@4.24.0): - dependencies: - esbuild: 0.24.2 - rollup: 4.24.0 - - rollup-plugin-polyfill-node@0.13.0(rollup@4.24.0): + rollup-plugin-esbuild-minify@1.3.0(rollup@4.40.2): dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.24.0) - rollup: 4.24.0 + esbuild: 0.25.4 + rollup: 4.40.2 - rollup@4.24.0: + rollup-plugin-polyfill-node@0.13.0(rollup@4.40.2): dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.0 - '@rollup/rollup-android-arm64': 4.24.0 - '@rollup/rollup-darwin-arm64': 4.24.0 - '@rollup/rollup-darwin-x64': 4.24.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 - '@rollup/rollup-linux-arm-musleabihf': 4.24.0 - '@rollup/rollup-linux-arm64-gnu': 4.24.0 - '@rollup/rollup-linux-arm64-musl': 4.24.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 - '@rollup/rollup-linux-riscv64-gnu': 4.24.0 - '@rollup/rollup-linux-s390x-gnu': 4.24.0 - '@rollup/rollup-linux-x64-gnu': 4.24.0 - '@rollup/rollup-linux-x64-musl': 4.24.0 - '@rollup/rollup-win32-arm64-msvc': 4.24.0 - '@rollup/rollup-win32-ia32-msvc': 4.24.0 - '@rollup/rollup-win32-x64-msvc': 4.24.0 - fsevents: 2.3.3 + '@rollup/plugin-inject': 5.0.5(rollup@4.40.2) + rollup: 4.40.2 - rollup@4.40.0: + rollup@4.40.2: dependencies: '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.40.0 - '@rollup/rollup-android-arm64': 4.40.0 - '@rollup/rollup-darwin-arm64': 4.40.0 - '@rollup/rollup-darwin-x64': 4.40.0 - '@rollup/rollup-freebsd-arm64': 4.40.0 - '@rollup/rollup-freebsd-x64': 4.40.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.0 - '@rollup/rollup-linux-arm-musleabihf': 4.40.0 - '@rollup/rollup-linux-arm64-gnu': 4.40.0 - '@rollup/rollup-linux-arm64-musl': 4.40.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0 - '@rollup/rollup-linux-riscv64-gnu': 4.40.0 - '@rollup/rollup-linux-riscv64-musl': 4.40.0 - '@rollup/rollup-linux-s390x-gnu': 4.40.0 - '@rollup/rollup-linux-x64-gnu': 4.40.0 - '@rollup/rollup-linux-x64-musl': 4.40.0 - '@rollup/rollup-win32-arm64-msvc': 4.40.0 - '@rollup/rollup-win32-ia32-msvc': 4.40.0 - '@rollup/rollup-win32-x64-msvc': 4.40.0 + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 fsevents: 2.3.3 rope-sequence@1.3.4: {} @@ -14517,7 +11059,7 @@ snapshots: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.1: + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -14525,7 +11067,13 @@ snapshots: safe-buffer@5.2.1: {} - safe-regex2@4.0.0: + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-regex2@5.0.0: dependencies: ret: 0.5.0 @@ -14537,8 +11085,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - secure-compare@3.0.1: {} - secure-json-parse@2.7.0: {} secure-json-parse@4.0.0: {} @@ -14549,7 +11095,7 @@ snapshots: semver@7.6.3: {} - semver@7.7.1: {} + semver@7.7.2: {} sequelize-pool@7.1.0: {} @@ -14577,15 +11123,15 @@ snapshots: transitivePeerDependencies: - supports-color - set-cookie-parser@2.7.0: {} + set-cookie-parser@2.7.1: {} set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -14615,8 +11161,6 @@ snapshots: shebang-regex@3.0.0: {} - shell-exec@1.0.2: {} - shimmer@1.2.1: {} short-unique-id@5.3.2: {} @@ -14641,13 +11185,6 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.2 - side-channel@1.1.0: dependencies: es-errors: 1.3.0 @@ -14664,8 +11201,8 @@ snapshots: sirv@3.0.1: dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.0 + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 totalist: 3.0.1 slash@3.0.0: {} @@ -14688,16 +11225,6 @@ snapshots: source-map-js@1.2.1: {} - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map-support@0.5.19: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -14710,16 +11237,16 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.21 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.21 - spdx-license-ids@3.0.20: {} + spdx-license-ids@3.0.21: {} split2@3.2.2: dependencies: @@ -14731,14 +11258,8 @@ snapshots: dependencies: through: 2.3.8 - sprintf-js@1.0.3: {} - sqlstring@2.3.3: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - stackback@0.0.2: {} standard-version@9.5.0: @@ -14754,7 +11275,7 @@ snapshots: figures: 3.2.0 find-up: 5.0.0 git-semver-tags: 4.1.1 - semver: 7.6.3 + semver: 7.7.2 stringify-package: 1.0.1 yargs: 16.2.0 @@ -14766,17 +11287,13 @@ snapshots: dependencies: bl: 5.1.0 - stop-iteration-iterator@1.0.0: + stop-iteration-iterator@1.1.0: dependencies: - internal-slot: 1.0.7 + es-errors: 1.3.0 + internal-slot: 1.1.0 string-argv@0.3.2: {} - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -14792,7 +11309,7 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.4.0 - get-east-asian-width: 1.2.0 + get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 string_decoder@1.1.1: @@ -14805,10 +11322,6 @@ snapshots: stringify-package@1.0.1: {} - strip-ansi@3.0.1: - dependencies: - ansi-regex: 2.1.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -14819,8 +11332,6 @@ snapshots: strip-bom@3.0.0: {} - strip-bom@4.0.0: {} - strip-final-newline@2.0.0: {} strip-indent@3.0.0: @@ -14829,14 +11340,14 @@ snapshots: strip-json-comments@3.1.1: {} - styled-components@5.3.9(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1): + styled-components@5.3.9(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1): dependencies: - '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) - '@babel/traverse': 7.26.5(supports-color@5.5.0) + '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) + '@babel/traverse': 7.27.1(supports-color@5.5.0) '@emotion/is-prop-valid': 1.3.1 '@emotion/stylis': 0.8.5 '@emotion/unitless': 0.7.5 - babel-plugin-styled-components: 2.1.4(@babel/core@7.26.0)(styled-components@5.3.9(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1))(supports-color@5.5.0) + babel-plugin-styled-components: 2.1.4(@babel/core@7.27.1)(styled-components@5.3.9(@babel/core@7.27.1)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1))(supports-color@5.5.0) css-to-react-native: 3.2.0 hoist-non-react-statics: 3.3.2 react: 18.3.1 @@ -14867,16 +11378,14 @@ snapshots: sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 ts-interface-checker: 0.1.13 - supports-color@2.0.0: {} - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -14885,40 +11394,10 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.11.1: - dependencies: - '@pkgr/core': 0.2.0 - tslib: 2.8.1 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.4 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - tarn@3.0.2: {} - tcp-port-used@1.0.2: - dependencies: - debug: 4.3.1 - is2: 2.0.9 - transitivePeerDependencies: - - supports-color - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - test-exclude@7.0.1: dependencies: '@istanbuljs/schema': 0.1.3 @@ -14931,8 +11410,6 @@ snapshots: text-mask-addons@3.8.0: {} - text-table@0.2.0: {} - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -14971,7 +11448,7 @@ snapshots: fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 - tinymce@7.9.0: {} + tinymce@6.8.5: {} tinypool@1.0.2: {} @@ -14987,10 +11464,6 @@ snapshots: dependencies: os-tmpdir: 1.0.2 - tmp@0.2.3: {} - - tmpl@1.0.5: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -15005,26 +11478,16 @@ snapshots: tr46@0.0.3: {} - tree-kill@1.2.2: {} - trim-newlines@3.0.1: {} - ts-api-utils@1.3.0(typescript@5.8.3): - dependencies: - typescript: 5.8.3 - - ts-api-utils@2.1.0(typescript@5.8.3): - dependencies: - typescript: 5.8.3 - ts-interface-checker@0.1.13: {} ts-morph@25.0.1: dependencies: - '@ts-morph/common': 0.26.0 + '@ts-morph/common': 0.26.1 code-block-writer: 13.0.3 - ts-node@10.9.2(@swc/core@1.11.22(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.11.24(@swc/helpers@0.5.17))(@types/node@22.15.18)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -15032,7 +11495,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 22.15.18 - acorn: 8.12.1 + acorn: 8.14.1 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 @@ -15042,35 +11505,21 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.11.22(@swc/helpers@0.5.17) - - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 + '@swc/core': 1.11.24(@swc/helpers@0.5.17) tslib@2.8.1: {} tsx@4.19.4: dependencies: - esbuild: 0.25.0 - get-tsconfig: 4.8.1 + esbuild: 0.25.4 + get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 type-assertions@1.1.0: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-detect@4.0.8: {} - type-fest@0.18.1: {} - type-fest@0.20.2: {} - type-fest@0.21.3: {} type-fest@0.6.0: {} @@ -15084,14 +11533,6 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint-language-service@5.0.5(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): - dependencies: - '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.27.0(jiti@2.4.2) - typescript: 5.8.3 - - typescript@5.7.3: {} - typescript@5.8.3: {} uc.micro@2.1.0: {} @@ -15116,15 +11557,11 @@ snapshots: unicorn-magic@0.1.0: {} - union@0.5.0: - dependencies: - qs: 6.14.0 - universalify@2.0.1: {} - update-browserslist-db@1.1.2(browserslist@4.24.4): + update-browserslist-db@1.1.3(browserslist@4.24.5): dependencies: - browserslist: 4.24.4 + browserslist: 4.24.5 escalade: 3.2.0 picocolors: 1.1.1 @@ -15132,23 +11569,17 @@ snapshots: dependencies: camelcase: 4.1.0 - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - url-join@4.0.1: {} - - use-isomorphic-layout-effect@1.2.0(@types/react@19.0.4)(react@18.3.1): + use-isomorphic-layout-effect@1.2.0(@types/react@19.1.4)(react@18.3.1): dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.1.4 use-memo-one@1.1.3(react@18.3.1): dependencies: react: 18.3.1 - use-sync-external-store@1.4.0(react@18.3.1): + use-sync-external-store@1.5.0(react@18.3.1): dependencies: react: 18.3.1 @@ -15160,19 +11591,11 @@ snapshots: v8-compile-cache-lib@3.0.1: {} - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 - validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - validate-npm-package-name@5.0.1: {} - validator@13.12.0: {} vary@1.1.2: {} @@ -15200,11 +11623,11 @@ snapshots: vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.7.1): dependencies: - esbuild: 0.25.0 + esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.3 - rollup: 4.40.0 + rollup: 4.40.2 tinyglobby: 0.2.13 optionalDependencies: '@types/node': 22.15.18 @@ -15223,7 +11646,7 @@ snapshots: '@vitest/spy': 3.1.3 '@vitest/utils': 3.1.3 chai: 5.2.0 - debug: 4.4.0 + debug: 4.4.1(supports-color@5.5.0) expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 @@ -15256,25 +11679,8 @@ snapshots: void-elements@3.1.0: {} - vue-eslint-parser@9.4.3(eslint@8.57.1): - dependencies: - debug: 4.4.1(supports-color@5.5.0) - eslint: 8.57.1 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - lodash: 4.17.21 - semver: 7.7.1 - transitivePeerDependencies: - - supports-color - w3c-keyname@2.2.8: {} - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - warning@4.0.3: dependencies: loose-envify: 1.4.0 @@ -15285,36 +11691,34 @@ snapshots: webidl-conversions@3.0.1: {} - whatwg-encoding@2.0.0: - dependencies: - iconv-lite: 0.6.3 - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-boxed-primitive@1.0.2: + which-boxed-primitive@1.1.1: dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 - is-weakset: 2.0.3 + is-weakset: 2.0.4 - which-typed-array@1.1.15: + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 which@1.3.1: @@ -15338,8 +11742,6 @@ snapshots: dependencies: '@types/node': 22.15.18 - word-wrap@1.2.5: {} - wordwrap@1.0.0: {} wrap-ansi@6.2.0: @@ -15368,11 +11770,6 @@ snapshots: wrappy@1.0.2: {} - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - xss@1.0.15: dependencies: commander: 2.20.3 @@ -15388,8 +11785,6 @@ snapshots: yaml@1.10.2: {} - yaml@2.7.0: {} - yaml@2.7.1: {} yargs-parser@20.2.9: {} @@ -15420,6 +11815,6 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.1.1: {} + yocto-queue@1.2.1: {} yoctocolors-cjs@2.1.2: {} diff --git a/src/chat/inGameEmails.ts b/src/chat/inGameEmails.ts index a22a8d4a0..c00cf6962 100644 --- a/src/chat/inGameEmails.ts +++ b/src/chat/inGameEmails.ts @@ -1,112 +1,114 @@ -import { ChatMessage } from "./ChatMessage.js"; -import { assertLength } from "./assertLength.js"; -import { ListInGameEmailsMessage } from "./ListInGameEmailsMessage.js"; -import { ListInGameEmailsResponseMessage } from "./ListInGameEmailsResponseMessage.js"; -import { InGameEmailMessage } from "./InGameEmailMessage.js"; -import { getServerLogger } from "rusty-motors-shared"; - -const defaultLogger = getServerLogger("chat.inGameEmails"); - +import { ChatMessage } from './ChatMessage.js'; +import { assertLength } from './assertLength.js'; +import { ListInGameEmailsMessage } from './ListInGameEmailsMessage.js'; +import { ListInGameEmailsResponseMessage } from './ListInGameEmailsResponseMessage.js'; +import { InGameEmailMessage } from './InGameEmailMessage.js'; +import { getServerLogger } from 'rusty-motors-logger'; +const defaultLogger = getServerLogger('chat.inGameEmails'); const unseenMail = new Map(); unseenMail.set( - 1, - new InGameEmailMessage({ - mailId: 1, - senderId: 1, - senderName: "System", - title: "Test Email", - sendTime: Date.now() * 0.001, - expireTime: Date.now() * 0.001 + 1000 * 60 * 60 * 24, - isUnread: true, - body: "This is a test email", - }), + 1, + new InGameEmailMessage({ + mailId: 1, + senderId: 1, + senderName: 'System', + title: 'Test Email', + sendTime: Date.now() * 0.001, + expireTime: Date.now() * 0.001 + 1000 * 60 * 60 * 24, + isUnread: true, + body: 'This is a test email', + }), ); export class ReceiveEmailMessage extends ChatMessage { - gameUserId: number; - mailId: number; - headerOnly: boolean; + gameUserId: number; + mailId: number; + headerOnly: boolean; - static override fromBuffer(buffer: Buffer): ReceiveEmailMessage { - const messageId = buffer.readUInt16BE(0); - const messageLength = buffer.readUInt16BE(2); + static override fromBuffer(buffer: Buffer): ReceiveEmailMessage { + const messageId = buffer.readUInt16BE(0); + const messageLength = buffer.readUInt16BE(2); - assertLength(buffer.byteLength, messageLength); + assertLength(buffer.byteLength, messageLength); - const payload = buffer.subarray(4, 4 + messageLength); + const payload = buffer.subarray(4, 4 + messageLength); - return new ReceiveEmailMessage(messageId, messageLength, payload); - } + return new ReceiveEmailMessage(messageId, messageLength, payload); + } - constructor(messageId: number, messageLength: number, payload: Buffer) { - super(messageId, messageLength, payload); + constructor(messageId: number, messageLength: number, payload: Buffer) { + super(messageId, messageLength, payload); - this.gameUserId = payload.readUInt32BE(0); - this.mailId = payload.readUInt16BE(4); - this.headerOnly = payload.readUInt16BE(8) === 1; - } + this.gameUserId = payload.readUInt32BE(0); + this.mailId = payload.readUInt16BE(4); + this.headerOnly = payload.readUInt16BE(8) === 1; + } - override toString(): string { - return `ReceiveEmailMessage: gameUserId=${this.gameUserId}, mailId=${this.mailId}, headerOnly=${this.headerOnly}`; - } + override toString(): string { + return `ReceiveEmailMessage: gameUserId=${this.gameUserId}, mailId=${this.mailId}, headerOnly=${this.headerOnly}`; + } } export function handleListInGameEmailsMessage(message: ChatMessage): Buffer[] { - defaultLogger.debug(`Handling ListInGameEmailsMessage: ${message.toString()}`); + defaultLogger.debug( + `Handling ListInGameEmailsMessage: ${message.toString()}`, + ); - const parsedMessage = ListInGameEmailsMessage.fromBuffer(message.toBuffer()); + const parsedMessage = ListInGameEmailsMessage.fromBuffer( + message.toBuffer(), + ); - defaultLogger.debug(`Parsed message: ${parsedMessage.toString()}`); + defaultLogger.debug(`Parsed message: ${parsedMessage.toString()}`); - const totalEmails = unseenMail.size; - const mailId = totalEmails > 0 ? unseenMail.keys().next().value || 0 : 0; + const totalEmails = unseenMail.size; + const mailId = totalEmails > 0 ? unseenMail.keys().next().value || 0 : 0; - const response = new ListInGameEmailsResponseMessage(totalEmails, mailId); + const response = new ListInGameEmailsResponseMessage(totalEmails, mailId); - defaultLogger.debug(`Response: ${response.toString()}`); + defaultLogger.debug(`Response: ${response.toString()}`); - return [response.toBuffer()]; + return [response.toBuffer()]; } export function handleReceiveEmailMessage(message: ChatMessage): Buffer[] { - defaultLogger.debug(`Handling ReceiveEmailMessage: ${message.toString()}`); + defaultLogger.debug(`Handling ReceiveEmailMessage: ${message.toString()}`); - const parsedMessage = ReceiveEmailMessage.fromBuffer(message.toBuffer()); + const parsedMessage = ReceiveEmailMessage.fromBuffer(message.toBuffer()); - defaultLogger.debug(`Parsed message: ${parsedMessage.toString()}`); + defaultLogger.debug(`Parsed message: ${parsedMessage.toString()}`); - const requestedEmail = unseenMail.get(parsedMessage.mailId); + const requestedEmail = unseenMail.get(parsedMessage.mailId); - if (!requestedEmail) { - defaultLogger.warn(`Email with ID ${parsedMessage.mailId} not found`); - return []; - } + if (!requestedEmail) { + defaultLogger.warn(`Email with ID ${parsedMessage.mailId} not found`); + return []; + } - const email = requestedEmail; + const email = requestedEmail; - if (!parsedMessage.headerOnly) { - defaultLogger.debug(`Email body requested`); - } + if (!parsedMessage.headerOnly) { + defaultLogger.debug("Email body requested"); + } - const buffers: Buffer[] = []; + const buffers: Buffer[] = []; - if (parsedMessage.headerOnly) { - buffers.push(email.toBuffer()); - } else { - buffers.push(email.toBuffer()); - } + if (parsedMessage.headerOnly) { + buffers.push(email.toBuffer()); + } else { + buffers.push(email.toBuffer()); + } - return buffers; + return buffers; } export function reverseBytes(value: number): number { - // Given an int, reverse the byte order - return ( - ((value & 0xff) << 24) | - ((value & 0xff00) << 8) | - ((value & 0xff0000) >> 8) | - ((value & 0xff000000) >> 24) - ); + // Given an int, reverse the byte order + return ( + ((value & 0xff) << 24) | + ((value & 0xff00) << 8) | + ((value & 0xff0000) >> 8) | + ((value & 0xff000000) >> 24) + ); } diff --git a/src/chat/index.ts b/src/chat/index.ts index 5c5f8c811..d50dc3cde 100644 --- a/src/chat/index.ts +++ b/src/chat/index.ts @@ -1,24 +1,20 @@ +import { SerializedBufferOld, type ServiceResponse } from 'rusty-motors-shared'; +import { type BufferSerializer } from 'rusty-motors-shared-packets'; +import { ChatMessage } from './ChatMessage.js'; import { - SerializedBufferOld, - type ServiceResponse, -} from "rusty-motors-shared"; -import { type BufferSerializer } from "rusty-motors-shared-packets"; -import { ChatMessage } from "./ChatMessage.js"; -import { - handleListInGameEmailsMessage, - handleReceiveEmailMessage, -} from "./inGameEmails.js"; -import { bufferToHexString } from "./toHexString.js"; -import * as Sentry from "@sentry/node"; -import { getServerLogger } from "rusty-motors-shared"; + handleListInGameEmailsMessage, + handleReceiveEmailMessage, +} from './inGameEmails.js'; +import { bufferToHexString } from './toHexString.js'; +import * as Sentry from '@sentry/node'; +import { getServerLogger } from 'rusty-motors-logger'; -const defaultLogger = getServerLogger("chat"); +const defaultLogger = getServerLogger('chat'); const handlers = new Map Buffer[]>(); handlers.set(0x0524, handleReceiveEmailMessage); handlers.set(0x0526, handleListInGameEmailsMessage); - /** * Receive chat data * @@ -27,59 +23,59 @@ handlers.set(0x0526, handleListInGameEmailsMessage); * @returns Service response */ async function receiveChatData({ - connectionId, - message, + connectionId, + message, }: { - connectionId: string; - message: BufferSerializer; + connectionId: string; + message: BufferSerializer; }): Promise { - defaultLogger.info(`Received chat data from connection ${connectionId}`); - defaultLogger.debug(`Message: ${message.toHexString()}`); + defaultLogger.info(`Received chat data from connection ${connectionId}`); + defaultLogger.debug(`Message: ${message.toHexString()}`); + + let inboundMessage: ChatMessage; - let inboundMessage: ChatMessage; - - try { - inboundMessage = ChatMessage.fromBuffer(message.serialize()); - } catch (error) { - const err = new Error(`[${connectionId}] Error deserializing message`, { - cause: error, - }); - defaultLogger.error(err.message); - Sentry.captureException(err); - return { - connectionId, - messages: [], - }; - } - defaultLogger.debug(`Deserialized message: ${inboundMessage.toString()}`); + try { + inboundMessage = ChatMessage.fromBuffer(message.serialize()); + } catch (error) { + const err = new Error(`[${connectionId}] Error deserializing message`, { + cause: error, + }); + defaultLogger.error(err.message); + Sentry.captureException(err); + return { + connectionId, + messages: [], + }; + } + defaultLogger.debug(`Deserialized message: ${inboundMessage.toString()}`); - const id = inboundMessage.messageId; + const id = inboundMessage.messageId; - defaultLogger.debug(`Message ID: ${id}`); + defaultLogger.debug(`Message ID: ${id}`); - const handler = handlers.get(id); + const handler = handlers.get(id); - if (handler) { - defaultLogger.debug(`Handling message with ID ${id}`); - const responses = handler(inboundMessage); - defaultLogger.debug( - `Responses: ${responses.map((response) => bufferToHexString(response))}`, - ); - const messages = responses.map((response) => { - const responseBuffer = new SerializedBufferOld(); - responseBuffer._doDeserialize(response); - return responseBuffer; - }); + if (handler) { + defaultLogger.debug(`Handling message with ID ${id}`); + const responses = handler(inboundMessage); + defaultLogger.debug( + `Responses: ${responses.map((response) => bufferToHexString(response))}`, + ); + const messages = responses.map((response) => { + const responseBuffer = new SerializedBufferOld(); + responseBuffer._doDeserialize(response); + return responseBuffer; + }); - return { - connectionId, - messages, - }; - } + return { + connectionId, + messages, + }; + } - throw new Error( - `Unable to process chat data from connection ${connectionId}, data: ${message.toHexString()}`, - ); + throw new Error( + `Unable to process chat data from connection ${connectionId}, data: ${message.toHexString()}`, + ); } export { receiveChatData }; diff --git a/src/server.ts b/src/server.ts index fef62f675..ca576f3b2 100755 --- a/src/server.ts +++ b/src/server.ts @@ -14,66 +14,66 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import * as Sentry from "@sentry/node"; -import { Gateway } from "rusty-motors-gateway"; +import * as Sentry from '@sentry/node'; +import { Gateway } from 'rusty-motors-gateway'; import { - getServerLogger, - verifyLegacyCipherSupport, - getServerConfiguration, -} from "rusty-motors-shared"; -import { databaseService } from "rusty-motors-database"; + verifyLegacyCipherSupport, + getServerConfiguration, +} from 'rusty-motors-shared'; +import { databaseService } from 'rusty-motors-database'; +import { getServerLogger } from 'rusty-motors-logger'; function main() { - const coreLogger = getServerLogger("core"); + const coreLogger = getServerLogger('core'); - try { - verifyLegacyCipherSupport(); - if (!databaseService.isDatabaseConnected) { - coreLogger.fatal("Database connection failed. Exiting."); - process.exit(1); - } - } catch (err) { - coreLogger.fatal(`Error in core server: ${String(err)}`); - process.exitCode = 1; - return; - } + try { + verifyLegacyCipherSupport(); + if (!databaseService.isDatabaseConnected) { + coreLogger.fatal('Database connection failed. Exiting.'); + process.exit(1); + } + } catch (err) { + coreLogger.fatal(`Error in core server: ${String(err)}`); + process.exitCode = 1; + return; + } - try { - const config = getServerConfiguration(); - const sanitizedConfig = { - ...config, - certificateFile: "[REDACTED]", - privateKeyFile: "[REDACTED]", - publicKeyFile: "[REDACTED]", - }; - coreLogger.debug( - `Pre-flight checks passed. Starting server with config: ${JSON.stringify(sanitizedConfig)}`, - ); + try { + const config = getServerConfiguration(); + const sanitizedConfig = { + ...config, + certificateFile: '[REDACTED]', + privateKeyFile: '[REDACTED]', + publicKeyFile: '[REDACTED]', + }; + coreLogger.debug( + `Pre-flight checks passed. Starting server with config: ${JSON.stringify(sanitizedConfig)}`, + ); - const appLog = coreLogger.child({ - name: "app", - level: config.logLevel, - }); + const appLog = coreLogger.child({ + name: 'app', + level: config.logLevel, + }); - const listeningPortList = [ - 6660, 7003, 8228, 8226, 8227, 9000, 9001, 9002, 9003, 9004, 9005, 9006, - 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 43200, 43300, 43400, - 53303, - ]; + const listeningPortList = [ + 6660, 7003, 8228, 8226, 8227, 9000, 9001, 9002, 9003, 9004, 9005, + 9006, 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 43200, 43300, + 43400, 53303, + ]; - const gatewayServer = new Gateway({ - config, - log: appLog, - listeningPortList, - }); + const gatewayServer = new Gateway({ + config, + log: appLog, + listeningPortList, + }); - gatewayServer.start(); - } catch (err) { - Sentry.captureException(err); - coreLogger.fatal(`Error in core server: ${String(err)}`); - process.exitCode = 1; - return; - } + gatewayServer.start(); + } catch (err) { + Sentry.captureException(err); + coreLogger.fatal(`Error in core server: ${String(err)}`); + process.exitCode = 1; + return; + } } main(); diff --git a/tsconfig.base.json b/tsconfig.base.json index a1587c950..726550277 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,7 +1,7 @@ { "compilerOptions": { "lib": ["es2022"], - "module": "CommonJS", + "module": "NodeNext", "target": "es2022", "allowUnusedLabels": false, "allowUnreachableCode": false, @@ -22,6 +22,7 @@ "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, "noPropertyAccessFromIndexSignature": true, + "moduleResolution": "nodenext", "declaration": true, "declarationMap": true, "sourceMap": true, @@ -34,7 +35,7 @@ "skipLibCheck": true, "isolatedModules": true, "experimentalDecorators": true, - "emitDecoratorMetadata": true, + "emitDecoratorMetadata": true }, "files": [], "references": [