Тема
React frontend на esbuild
В корневой папке frontend находится рабочий consumer-проект:
- esbuild;
- TypeScript strict;
- React;
- MobX;
- mobx-react-lite;
- Axios;
- Valibot;
- локальный
@dubium/query-layerиз соседней папкиpackage.
Текущий consumer проверяется с MobX 7.0.3 и mobx-react-lite 5.0.3. mobx-react-lite 5 требует React 18+, а этот workspace использует React 19.
Запуск
Откройте два терминала из корня проекта.
Терминал 1:
bash
npm run backend:devТерминал 2:
bash
npm run frontend:devОткройте http://127.0.0.1:3200. Frontend обращается к Express API http://127.0.0.1:3100/api.
frontend:dev и frontend:lint сначала запускают lifecycle scripts predev и prelint, которые собирают соседний workspace @dubium/query-layer. После этого esbuild и TypeScript резолвят импорт пакета через собранный package/dist. Поэтому для первого запуска и CI lint не требуется вручную создавать dist, а workspace symlink из node_modules не может сослаться на ещё не собранную точку входа.
Ожидаемая строка после запуска:
text
Query Layer frontend: http://127.0.0.1:3200Не помечайте @dubium/query-layer как external: браузер не сможет разрешить такой bare import без отдельной import map.
Проверки
bash
npm run frontend:typecheck
npm run frontend:buildОбщая команда workspace также включает frontend:
bash
npm run typecheck
npm run buildСтруктура
text
frontend/
├── esbuild.config.mjs
├── index.html
├── package.json
├── tsconfig.json
└── src/
├── app/App.tsx
├── data/
│ ├── api.ts
│ ├── token/
│ ├── users/
│ ├── profile/
│ └── diagnostics/
├── shared/config.ts
├── main.tsx
└── styles.cssПроверяемые сценарии
Список и создание пользователей
UsersStore композиционно использует два handler:
createQueryStoreавтоматически загружает список;createRequestStoreвыполняет команду создания;- после успешной команды business store вызывает refetch списка;
- React читает только
users,loading,creating,statusиerror.
Private profile и refresh
ProfileStore использует FetchStore. Первый request отправляет просроченный access-v1 и получает 401. authApi выполняет refresh, сохраняет access-v2 и один раз повторяет profile request. UI получает только итоговое MobX-состояние. В этом demo внешняя обёртка сохраняет предметное поле profile и единый lifecycle feature. Для простого GET без такой логики можно экспортировать настроенный FetchStore напрямую, как описано в странице FetchStore.
Отмена
SlowRequestStore использует createRequestStore. Метод cancel() отменяет Axios request через автоматически добавленный AbortSignal. Express backend фиксирует закрытие соединения.
API-функции не импортируются React-компонентом
App.tsx импортирует:
ts
import { usersStore } from "../data/users"
import { profileStore } from "../data/profile"
import { slowRequestStore } from "../data/diagnostics"Он не импортирует:
ts
import { publicApi, authApi } from "../data/api"
import { getUsersApi } from "../data/users/api/users.api"Это и есть проверяемая архитектурная граница проекта.
CORS
Backend разрешает local origins:
http://127.0.0.1:3200;localhostна порту3200.
Preflight для JSON и Authorization headers проверяется backend-тестом.