npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@elcrm/db

v0.1.0

Published

Bun SQL MySQL/Postgres + bun:sqlite for elCRM

Readme

@elcrm/db

Библиотека для работы с MySQL, PostgreSQL и SQLite через Bun. Один и тот же async API (db.query / insert / get / transaction / ensureColumn): MySQL/Postgres — Bun SQL, SQLite — bun:sqlite с bind-параметрами.

Для поэтапной миграции с «голого» bun:sqlite есть sync escape hatch: getSqlite().

📖 Полная техническая документация API доступна в API.md

Установка

npm install @elcrm/db
# или
bun add @elcrm/db

Настройка

Библиотека автоматически использует переменные окружения для подключения к базе данных. Вы можете настроить их следующими способами:

Переменные окружения

Библиотека автоматически определяет тип базы данных (MySQL или PostgreSQL) на основе переменных окружения или конфигурации.

Для MySQL:

# Вариант 1: Стандартные переменные
DB_TYPE=mysql
DB_HOST=localhost
DB_PORT=3306
DB_BASE=name_base
DB_USER=root
DB_PASS=password

# Вариант 2: MYSQL_ префикс
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_DATABASE=name_base
MYSQL_USER=root
MYSQL_PASSWORD=password

Для PostgreSQL:

# Вариант 1: Стандартные переменные
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_BASE=name_base
DB_USER=postgres
DB_PASS=password

# Вариант 2: POSTGRES_ или PG_ префикс
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DATABASE=name_base
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password

# Или с PG_ префиксом
PG_HOST=localhost
PG_PORT=5432
PG_DATABASE=name_base
PG_USER=postgres
PG_PASSWORD=password

Для SQLite:

DB_TYPE=sqlite
DB_BASE=/path/to/data.db
# или
SQLITE_PATH=/path/to/data.db
import { db, initDB, getSqlite } from "@elcrm/db";

await initDB({ type: "sqlite", database: "./data.db" });
// или :memory: / sqlite:///abs/path.db / file:./data.db

await db.ensureColumn("users", "avatar", "TEXT NOT NULL DEFAULT ''");
const raw = getSqlite(); // bun:sqlite Database

Примечание: Если DB_TYPE не указан, по умолчанию используется MySQL.

Использование

Базовое подключение

import { db } from "@elcrm/db";

// Проверка подключения
const isConnected = await db.ping();
if (!isConnected) {
    console.error("Не удалось подключиться к базе данных");
}

API

Основные методы

query<T>(sql: string, params?: any[]): Promise<T[]>

Выполняет произвольный SQL-запрос с параметрами.

// Простой запрос
const users = await db.query("SELECT * FROM users");

// С параметрами
const user = await db.query<User>(
    "SELECT * FROM users WHERE id = ? AND status = ?",
    [1, "active"]
);

// Типизированный результат
interface User {
    id: number;
    name: string;
    email: string;
}

const typedUsers = await db.query<User>("SELECT * FROM users");

insert(table: string, data: RecordData): Promise<InsertResult>

Вставляет новую запись в таблицу.

const result = await db.insert("users", {
    name: "Иван",
    email: "[email protected]",
    age: 25,
});

console.log(result.last_insert_id); // ID вставленной записи

selectWhere<T>(table: string, columns: string[], where?: WhereCondition, options?: SelectOptions): Promise<T[]>

Выполняет SELECT-запрос с условиями WHERE.

// Простой запрос
const users = await db.selectWhere("users", ["id", "name", "email"]);

// С условиями WHERE
const activeUsers = await db.selectWhere<User>(
    "users",
    ["id", "name", "email"],
    { status: "active", age: 25 }
);

// С опциями сортировки и лимита
const topUsers = await db.selectWhere<User>(
    "users",
    ["id", "name", "email"],
    { status: "active" },
    { orderBy: "created_at DESC", limit: 10 }
);

update(table: string, data: RecordData, where: WhereCondition): Promise<QueryResult[]>

Обновляет записи в таблице.

// Обновление одной записи
await db.update(
    "users",
    { name: "Новое имя", email: "[email protected]" },
    { id: 1 }
);

// Обновление нескольких записей
await db.update(
    "users",
    { status: "inactive" },
    { age: 18, status: "pending" }
);

select<T>(table: string, columns: string[], whereClause?: string): Promise<T[]>

Выполняет SELECT-запрос с произвольным WHERE условием.

// Без условий
const allUsers = await db.select("users", ["id", "name"]);

// С произвольным WHERE
const users = await db.select<User>(
    "users",
    ["id", "name", "email"],
    "age > 18 AND status = 'active'"
);

find<T>(table: string, column: string, value: any): Promise<T[]>

Находит записи по значению столбца.

// Поиск по ID
const user = await db.find<User>("users", "id", 1);

// Поиск по email
const users = await db.find<User>("users", "email", "[email protected]");

exists(table: string, column: string, value: any): Promise<{ exists: number }[]>

Проверяет существование записи.

const result = await db.exists("users", "email", "[email protected]");
if (result[0].exists === 1) {
    console.log("Пользователь существует");
}

get<T>(sql: string, params?: any[]): Promise<T | null>

Получает одну запись из результата запроса.

const user = await db.get<User>("SELECT * FROM users WHERE id = ?", [1]);

if (user) {
    console.log(user.name);
}

run(sql: string, params?: any[]): Promise<RunResult>

Выполняет SQL-запрос и возвращает метаданные.

const result = await db.run("UPDATE users SET status = ? WHERE id = ?", [
    "active",
    1,
]);

console.log(result.affectedRows); // Количество затронутых строк
console.log(result.insertId); // ID вставки (если был INSERT)

lastInsertId(): Promise<InsertResult[]>

Получает последний вставленный ID.

const result = await db.lastInsertId();
console.log(result[0].last_insert_id);

Транзакции

transaction<T>(callback: (db: Database) => Promise<T>): Promise<T>

Выполняет операции в транзакции.

try {
    const result = await db.transaction(async (db) => {
        // Все операции выполняются в одной транзакции
        await db.insert("users", { name: "Иван", email: "[email protected]" });
        await db.insert("profiles", { user_id: 1, bio: "Описание" });

        return { success: true };
    });

    console.log("Транзакция выполнена успешно");
} catch (error) {
    console.error("Ошибка транзакции:", error);
    // Автоматический ROLLBACK выполнен
}

Утилиты

ping(): Promise<boolean>

Проверяет подключение к базе данных.

const isConnected = await db.ping();
if (isConnected) {
    console.log("База данных доступна");
}

getTableStructure(table: string): Promise<TableColumnInfo[]>

Получает структуру таблицы из базы данных.

const structure = await db.getTableStructure("users");
structure.forEach((column) => {
    console.log(`${column.Field}: ${column.Type}`);
});

checkTableStructure(table: string, typeStructure: TypeStructure): Promise<StructureCheckResult>

Проверяет соответствие структуры таблицы TypeScript типу.

import { db, createTypeStructure } from "@elcrm/db";

const structure = createTypeStructure({
    id: "number",
    name: "string",
    email: "string",
});

const result = await db.checkTableStructure("users", structure);
if (result.isValid) {
    console.log("✅ Структура соответствует!");
} else {
    console.log("❌ Обнаружены несоответствия:", result);
}

createTableFromType(table: string, typeStructure: TypeStructure, options?: CreateTableOptions): Promise<QueryResult[]>

Создает таблицу в базе данных на основе TypeScript типа.

import { db, createTypeStructure } from "@elcrm/db";

const structure = createTypeStructure({
    id: "number",
    name: "string",
    email: "string",
});

await db.createTableFromType("users", structure, {
    timestamps: true,
});

Date(date?: string | number): string

Форматирует дату для базы данных (MySQL/PostgreSQL).

// Текущая дата
const now = db.Date(); // "2024-01-15 12:30:45"

// Конкретная дата
const date = db.Date("2024-01-15T12:30:45Z"); // "2024-01-15 12:30:45"

type: DatabaseType

Получает тип подключенной базы данных.

console.log(db.type); // "mysql" или "postgres"

parserCell

Утилиты для работы с ячейками специального формата.

toBase(arr: number[]): string

Преобразует массив чисел в строку формата .1.2.3.

const str = db.parserCell.toBase([1, 2, 3, 4, 5]);
console.log(str); // ".1.2.3.4.5."
toArray(str: string): number[]

Преобразует строку формата .1.2.3. в массив чисел.

const arr = db.parserCell.toArray(".1.2.3.4.5.");
console.log(arr); // [1, 2, 3, 4, 5]

Создание таблиц из SQL файлов

Библиотека предоставляет удобные функции для создания таблиц из SQL файлов с автоматическим преобразованием MySQL синтаксиса в PostgreSQL при необходимости.

ensureTableFromSql(tableName: string, sqlFilePath: string, onStatus?: (status: string) => void): Promise<void>

Создает таблицу из SQL файла, если её нет. Автоматически определяет тип БД и преобразует MySQL SQL в PostgreSQL синтаксис при необходимости.

import { ensureTableFromSql } from "@elcrm/db";
import { join } from "path";

// Создание таблицы из SQL файла
await ensureTableFromSql(
    "users",
    join(process.cwd(), "db", "users.sql"),
    (status) => console.log(status) // Опциональный колбэк для статуса
);

Особенности:

  • Автоматически проверяет существование таблицы перед созданием
  • Преобразует MySQL синтаксис в PostgreSQL при необходимости
  • Поддерживает колбэк для отслеживания статуса выполнения
  • Удаляет DROP TABLE IF EXISTS из SQL файла (используется проверка существования)

createTableSql(name: string, onStatus?: (status: string) => void): Promise<void>

Простейший фасад для создания таблицы из файла в папке db. Ищет файл db/<name>.sql и создает таблицу с именем name.

import { createTableSql } from "@elcrm/db";

// Создает таблицу users из файла db/users.sql
await createTableSql("users", (status) => console.log(status));

Пример структуры проекта:

project/
├── db/
│   ├── users.sql
│   ├── products.sql
│   └── orders.sql
└── src/
    └── index.ts

Пример SQL файла (MySQL синтаксис):

CREATE TABLE IF NOT EXISTS users (
    id INT(11) NOT NULL AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Библиотека автоматически преобразует этот SQL в PostgreSQL синтаксис при работе с PostgreSQL:

CREATE TABLE IF NOT EXISTS public.users (
    id SERIAL NOT NULL,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

Преобразования MySQL → PostgreSQL:

  • INT(11) AUTO_INCREMENTSERIAL
  • TINYINT(1)BOOLEAN
  • DATETIMETIMESTAMP
  • Обратные кавычки ` → двойные кавычки " или удаляются
  • ENGINE, CHARSET, COLLATE → удаляются
  • ON UPDATE CURRENT_TIMESTAMP → удаляется (PostgreSQL использует триггеры)
  • UNIQUE KEYUNIQUE
  • KEY (индексы) → удаляются (создаются отдельно в PostgreSQL)

Типы

Интерфейсы

// Конфигурация подключения
interface DBConfig {
    type?: DatabaseType; // "mysql" или "postgres" (по умолчанию "mysql")
    host?: string;
    port?: number;
    database?: string;
    user?: string;
    password?: string;
}

// Тип базы данных
type DatabaseType = "mysql" | "postgres";

// Результат запроса
interface QueryResult {
    [key: string]: any;
}

// Результат вставки
interface InsertResult {
    last_insert_id: number;
}

// Результат выполнения
interface RunResult {
    affectedRows: number;
    insertId: number | null;
}

// Опции для SELECT
interface SelectOptions {
    orderBy?: string;
    limit?: number;
}

// Данные для вставки/обновления
type RecordData = Record<string, any>;

// Условия WHERE
type WhereCondition = Record<string, any>;

// Информация о колонке таблицы
interface TableColumnInfo {
    Field: string;
    Type: string;
    Null: "YES" | "NO";
    Key: string;
    Default: string | null;
    Extra: string;
}

// Структура типа для проверки
type TypeStructure = Record<string, string | string[]>;

// Результат проверки структуры таблицы
interface StructureCheckResult {
    isValid: boolean;
    missingInDB: string[]; // Поля, которые есть в типе, но отсутствуют в БД
    missingInType: string[]; // Поля, которые есть в БД, но отсутствуют в типе
    typeMismatches: Array<{
        field: string;
        dbType: string;
        expectedType: string | string[];
    }>;
}

// Опции для создания таблицы
interface CreateTableOptions {
    primaryKey?: string; // Название поля для первичного ключа (по умолчанию "id")
    engine?: string; // Движок таблицы (по умолчанию "InnoDB")
    charset?: string; // Кодировка (по умолчанию "utf8mb4")
    collate?: string; // Сравнение (по умолчанию "utf8mb4_unicode_ci")
    ifNotExists?: boolean; // Использовать IF NOT EXISTS (по умолчанию true)
    timestamps?: boolean; // Автоматически добавлять created_at и updated_at (по умолчанию false)
}

// Описание колонки для создания таблицы
interface ColumnDefinition {
    name: string;
    type: string | string[]; // TypeScript тип
    nullable?: boolean; // Может быть null
    defaultValue?: any; // Значение по умолчанию
    autoIncrement?: boolean; // Автоинкремент
    primaryKey?: boolean; // Первичный ключ
    unique?: boolean; // Уникальное значение
    length?: number; // Длина для строковых типов
}

Примеры использования

Поддержка MySQL и PostgreSQL

Библиотека автоматически определяет тип базы данных и адаптирует запросы:

import { db } from "@elcrm/db";

// Автоматическое определение типа БД
console.log(db.type); // "mysql" или "postgres"

// Все методы работают одинаково для обеих БД
const users = await db.query("SELECT * FROM users");

// INSERT автоматически использует правильный синтаксис
// MySQL: SELECT LAST_INSERT_ID()
// PostgreSQL: RETURNING id
const result = await db.insert("users", {
    name: "Иван",
    email: "[email protected]",
});

Создание пользователя

import { db } from "@elcrm/db";

interface User {
    id: number;
    name: string;
    email: string;
    created_at: string;
}

// Создание
const result = await db.insert("users", {
    name: "Иван Иванов",
    email: "[email protected]",
});

const userId = result.last_insert_id;

// Получение созданного пользователя
const user = await db.get<User>("SELECT * FROM users WHERE id = ?", [userId]);

Поиск и фильтрация

// Поиск активных пользователей старше 18 лет
const activeUsers = await db.selectWhere<User>(
    "users",
    ["id", "name", "email"],
    { status: "active", age: 18 },
    { orderBy: "created_at DESC", limit: 20 }
);

// Проверка существования
const exists = await db.exists("users", "email", "[email protected]");
if (exists[0].exists === 1) {
    console.log("Пользователь уже существует");
}

Обновление данных

// Обновление профиля пользователя
await db.update(
    "users",
    {
        name: "Новое имя",
        updated_at: db.Date(),
    },
    { id: 1 }
);

Работа с транзакциями

// Создание пользователя с профилем в транзакции
try {
    const result = await db.transaction(async (db) => {
        // Создаем пользователя
        const userResult = await db.insert("users", {
            name: "Иван",
            email: "[email protected]",
        });

        // Создаем профиль
        await db.insert("profiles", {
            user_id: userResult.last_insert_id,
            bio: "Описание профиля",
        });

        return userResult.last_insert_id;
    });

    console.log(`Пользователь создан с ID: ${result}`);
} catch (error) {
    console.error("Ошибка при создании пользователя:", error);
}

Использование с TypeScript

import { db, QueryResult } from "@elcrm/db";

// Определяем интерфейс для типизации
interface Product {
    id: number;
    name: string;
    price: number;
    category_id: number;
}

// Типизированные запросы
const products = await db.selectWhere<Product>(
    "products",
    ["id", "name", "price"],
    { category_id: 1 },
    { orderBy: "price ASC", limit: 10 }
);

// products имеет тип Product[]
products.forEach((product) => {
    console.log(product.name, product.price);
});

Создание таблиц из SQL файлов

import { createTableSql, ensureTableFromSql } from "@elcrm/db";

// Простой способ - из папки db/
await createTableSql("users", (status) => {
    console.log(status); // Логирование статуса
});

// Продвинутый способ - с указанием полного пути
await ensureTableFromSql("products", "/path/to/products.sql", (status) =>
    console.log(status)
);

Проверка соответствия структуры таблицы TypeScript типу

Библиотека предоставляет возможность проверить, соответствует ли структура таблицы в базе данных вашему TypeScript типу.

import { db, createTypeStructure } from "@elcrm/db";

// Ваш TypeScript тип
export type TUsers = {
    id: number;
    tid: number;
    username: string;
    version: string;
    online: Date | string;
    lang: string;
    first_name: string;
    last_name: string;
    premium: number;
    notice: number;
    agent: string;
    avatar: string;
    created_at: Date | string;
    birthday: Date | string;
    sex: number;
    country_id: number;
    region_id: number;
    city_id: number;
    about: string;
    active: number;
    maths: number;
    subscription: number;
    search: number;
    updated_at: Date | string;
    agreement: string;
    phone: string;
    age: number;
    photos: string;
    current_id: number;
    height: number;
    alcohol: number;
    smoke: number;
    children: number;
    education: number;
    zodiac: number;
    interests: number;
    work: number;
    pets: number;
    ip: string;
};

// Создаем структуру для проверки
const usersStructure = createTypeStructure<TUsers>({
    id: "number",
    tid: "number",
    username: "string",
    version: "string",
    online: ["Date", "string"],
    lang: "string",
    first_name: "string",
    last_name: "string",
    premium: "number",
    notice: "number",
    agent: "string",
    avatar: "string",
    created_at: ["Date", "string"],
    birthday: ["Date", "string"],
    sex: "number",
    country_id: "number",
    region_id: "number",
    city_id: "number",
    about: "string",
    active: "number",
    maths: "number",
    subscription: "number",
    search: "number",
    updated_at: ["Date", "string"],
    agreement: "string",
    phone: "string",
    age: "number",
    photos: "string",
    current_id: "number",
    height: "number",
    alcohol: "number",
    smoke: "number",
    children: "number",
    education: "number",
    zodiac: "number",
    interests: "number",
    work: "number",
    pets: "number",
    ip: "string",
});

// Проверяем соответствие
const result = await db.checkTableStructure("users", usersStructure);

if (result.isValid) {
    console.log("✅ Структура таблицы соответствует типу!");
} else {
    console.log("❌ Обнаружены несоответствия:");

    if (result.missingInDB.length > 0) {
        console.log("Поля, отсутствующие в БД:", result.missingInDB);
    }

    if (result.missingInType.length > 0) {
        console.log("Поля, отсутствующие в типе:", result.missingInType);
    }

    if (result.typeMismatches.length > 0) {
        console.log("Несоответствия типов:");
        result.typeMismatches.forEach((mismatch) => {
            console.log(
                `  - ${mismatch.field}: БД имеет тип "${mismatch.dbType}", ожидается "${mismatch.expectedType}"`
            );
        });
    }
}

Получение структуры таблицы

Вы также можете получить структуру таблицы напрямую:

import { db, TableColumnInfo } from "@elcrm/db";

const structure = await db.getTableStructure("users");

structure.forEach((column) => {
    console.log(
        `${column.Field}: ${column.Type} (${
            column.Null === "YES" ? "nullable" : "not null"
        })`
    );
});

Создание таблицы из TypeScript типа

Библиотека может автоматически создавать таблицы в базе данных на основе вашего TypeScript типа:

import { db, createTypeStructure, CreateTableOptions } from "@elcrm/db";

// Ваш TypeScript тип
export type TUsers = {
    id: number;
    tid: number;
    username: string;
    email: string;
    first_name: string;
    last_name: string;
    age: number;
    active: number;
    created_at: Date | string;
    updated_at: Date | string;
};

// Создаем структуру для таблицы
const usersStructure = createTypeStructure<TUsers>({
    id: "number",
    tid: "number",
    username: "string",
    email: "string",
    first_name: "string",
    last_name: "string",
    age: "number",
    active: "number",
    created_at: ["Date", "string"],
    updated_at: ["Date", "string"],
});

// Опции создания таблицы
const options: CreateTableOptions = {
    primaryKey: "id", // Поле первичного ключа (по умолчанию "id")
    engine: "InnoDB", // Движок таблицы (по умолчанию "InnoDB")
    charset: "utf8mb4", // Кодировка (по умолчанию "utf8mb4")
    collate: "utf8mb4_unicode_ci", // Сравнение (по умолчанию "utf8mb4_unicode_ci")
    ifNotExists: true, // Использовать IF NOT EXISTS (по умолчанию true)
    timestamps: false, // Автоматически добавлять created_at и updated_at (по умолчанию false)
};

// Создаем таблицу
await db.createTableFromType("users", usersStructure, options);
console.log("✅ Таблица users создана успешно!");

Особенности автоматического создания:

  • Первичный ключ: Поле с названием, указанным в primaryKey (по умолчанию "id"), автоматически становится первичным ключом с AUTO_INCREMENT
  • Типы данных: Автоматически определяются на основе TypeScript типов:
    • numberINT
    • stringVARCHAR(255) или TEXT (в зависимости от названия поля)
    • Date | stringTIMESTAMP или DATE
    • booleanTINYINT(1)
  • Умное определение типов: Библиотека анализирует названия полей:
    • Поля с email, url, linkVARCHAR(255)
    • Поля с phone, telVARCHAR(20)
    • Поля с description, content, text, aboutTEXT
    • Поля с time, atTIMESTAMP
  • Timestamps: Если timestamps: true, автоматически добавляются поля created_at и updated_at с соответствующими значениями по умолчанию
  • Nullable: Поля с типом | null или | undefined могут быть NULL

Пример с автоматическими timestamps:

const simpleStructure = createTypeStructure({
    id: "number",
    name: "string",
    email: "string",
});

// Автоматически добавит created_at и updated_at
await db.createTableFromType("users", simpleStructure, {
    timestamps: true,
});

Продвинутое использование

Инициализация с конфигурацией

import { initDB } from "@elcrm/db";

// Инициализация с явной конфигурацией
await initDB({
    type: "postgres", // или "mysql"
    host: "localhost",
    port: 5432,
    database: "mydb",
    user: "postgres",
    password: "password",
});

Прямой доступ к подключению

import { getConnection } from "@elcrm/db";

const connection = getConnection();
// Используйте connection для прямых операций с SQL

Определение типа базы данных

import { db } from "@elcrm/db";

// Получить тип подключенной БД
console.log(db.type); // "mysql" или "postgres"

// Использовать в условной логике
if (db.type === "postgres") {
    // PostgreSQL-специфичная логика
} else {
    // MySQL-специфичная логика
}

Обработка ошибок

Все методы библиотеки могут выбрасывать исключения. Рекомендуется использовать try-catch:

try {
    const users = await db.query("SELECT * FROM users");
} catch (error) {
    console.error("Ошибка выполнения запроса:", error);
}

Лицензия

MIT

Автор

MaSkal [email protected]