Марсель Абазбеков

Full Stack разработчик

Fullstack-разработчик с опытом работы на TypeScript (Nest.js, Express, Prisma) и Go для бэкенда, включая работу с PostgreSQL, Redis, BullMQ и чистым SQL. Также разрабатывал небольшие сервисы на Python с FastAPI. На фронтенде использую React, Next.js, Electron и Effector вместе с сопутствующими технологиями.

Успешно применяю Git для организации рабочего процесса и контроля версий. Имею опыт работы с инструментами непрерывной интеграции и развертывания (CI/CD) GitHub Actions, GitLab CI.

Также иногда вношу вклад в open-source проекты, которыми пользуюсь.

Хочу развиваться в направлениях бэкенд- и фронтенд-разработки. Открыт к изучению новых языков программирования, платформ и технологий.

В 2024 закончил бакалавриат по программе «Программная инженерия» в НГТУ.

Навыки

Frontend

JavaScript, TypeScript, Electron, React, Next.js, Vite, Tailwind, Styled Components, SCSS, Redux, Effector, PWA, SSG

Backend

JavaScript, TypeScript, Node.js, Express.js, Nest.js, SQL, Prisma, PostgreSQL, Redis, BullMQ, Go, Gin, Gorm, REST API, GraphQL, SSE

Общие навыки

Git, Linux, Nginx, Docker, Ansible, GitLab CI, etc.

Другие технологии

C, C++, Java, Hibernate, JavaFX, Android apps on Android SDK, Python, Selenium, FastAPI, Vue

Вклад в opensource

Мой вклад в opensource проекты

TypeScript React Vite Node.js Python SSE SSG

Здесь представлен список моих принятых пулл реквестов в опенсорс проекты.

Wouter (2+ млн скачиваний в неделю)

Во время работы над дипломом я использовал лёгковесную библиотеку wouter для роутинга в Electron-приложении на React. Поскольку в проекте не было веб-сервера и всё работало через файловую систему, навигация строилась через hash в URL — обычный path-based роутинг в таких условиях не работает. Из-за этого я столкнулся с тем, что wouter не поддерживает search params для hash-роутера.

Сначала решил проблему локально, переопределив хук useHashLocation, а потом решил довести это до PR в основной репозиторий. Реализация на тот момент была не самой элегантной, но с помощью советов мейнтейнера wouter, Алексея Тактарова, я доработал решение — и в итоге поддержка search params при hash-матчинге путей была добавлена в библиотеку.

PR: https://github.com/molefrog/wouter/pull/465

Плагин vite-react-ssg

Во время работы в BotHub передо мной стояла задача внедрить SSG (static site generation) для SPA-приложения на vite+react. После анализа доступных инструментов я выбрал плагин vite-react-ssg как наиболее подходящий, но в процессе интеграции столкнулся с багом при совместной работе с vite-plugin-pwa: сборка падала с ошибкой “The URL must be of scheme file”.

Чтобы рендерить страницы во время сборки, vite-react-ssg по опции mock эмулирует браузерное окружение через jsdom с помощью утилиты jsdomGlobal — она подставляет объекты вроде document в глобальное окружение Node.js и возвращает функцию для очистки этих моков. Эта функция очистки нигде в коде плагина не вызывалась.

После того как все страницы отрендерены, vite-react-ssg сам вызывает regenerateSW у vite-plugin-pwa, чтобы пересобрать service worker. vite-plugin-pwa использует rollup-plugin-terser для минификации service worker’а, а terser определяет окружение по наличию объекта document: если document есть, он считает, что работает в браузере, и пытается резолвить путь к воркеру через document.baseURI. Из-за оставшихся моков terser ошибочно думал, что находится в браузере, и падал с ошибкой при резолвинге URL.

Я добавил вызов функции очистки моков перед вызовом regenerateSW, что исправило ошибку.

PR: https://github.com/Daydreamer-riri/vite-react-ssg/pull/59

gpt4free (66к звёзд)

gpt4free — open-source библиотека, агрегирующая доступ к разным LLM-провайдерам под единым API, в том числе к веб-версии ChatGPT через провайдер OpenaiChat.

Стриминговый API chatgpt.com устроен нестандартно: он отдаёт не текст, а последовательность операций, которые нужно применить на фронте, чтобы собрать сообщение — это позволяет модели не только дописывать ответ, но и переписывать уже показанное, вплоть до замены отдельного символа.

Каждая строка потока — это операция над накапливаемым ответом, с тремя полями: p — путь внутри структуры сообщения, куда она применяется (/message/content/parts/0 для текста, /message/metadata/refresh_key_info для служебных метаданных), o — тип операции (append — дописать значение к тому, что уже есть по этому пути; replace — заменить значение по пути целиком; patch — применить сразу несколько таких операций списком), и v — само значение. Из-за replace и patch модель может переписывать уже показанный текст, а не только дописывать новый. gpt4free разбирает поток построчно, собирая из него ответ в формате стрима Chat Completions API.

Исправил сериализацию None в строку “None” в поле content при стриминге — сериализатор стал возвращать пустую строку.

PR: https://github.com/xtekky/gpt4free/pull/2942

Если в диалоге использовалась генерация изображений, в потоке появлялась строка с p == “/message/metadata/refresh_key_info” и непустым v. Разбор в iter_messages_line проверял только, что p отсутствует или равен /message/content/parts/0, и в обоих случаях отдавал v как текст ответа — из-за чего значение refresh_key_info попадало в вывод пользователю.

Я добавил проверку на этот конкретный путь перед основной веткой: при совпадении в поток уходит пустая строка вместо содержимого этой строки.

PR: https://github.com/xtekky/gpt4free/pull/3026

Помимо путей вида p, в сам текстовый контент (v для /message/content/parts/0) модель встраивает escape-последовательности вида \ue200…\ue201 — ссылки на источники, картинки, видео, товары и погодные виджеты (turn0image0, turn0search3, turn0news5 и т.д.). Число после turn0search/turn0news в старой реализации использовалось прямо как индекс в списке источников, хотя реальное соответствие нужно искать по полям ref_index и ref_type, которые приходят в самих источниках отдельными строками потока. Из-за этого ссылки указывали не на те источники.

Отдельная проблема была в том, что одна строка потока могла нести сразу две последовательности подряд — одну завершённую и одну начатую, — а старый regex вырезал \ue200/\ue201 без разбора и ломал ещё не полученную до конца последовательность.

Я добавил класс ContentReferences, который сопоставляет ссылки на источники по ref_index/ref_type по мере прихода строк потока, и переработал regex так, чтобы заменялись только полностью завершённые последовательности, а начатая и ещё не закрытая оставалась в буфере до следующей строки. Отдельным коммитом поправил format_link: он не проверял, что заголовок ссылки — пустая строка, и в таком случае оставлял битую ссылку без текста.

PR: https://github.com/xtekky/gpt4free/pull/3071