Перейти к основному содержимому

@mineflow/api-schemas

L0-пакет курируемых browser/RN-safe контрактов: FSM-машины (xstate) и status-энумы доменов MineFlow. Это единственный безопасный для фронта способ получить ту же машину состояний, что используется на бэке — чтобы UI показывал ровно те действия, которые разрешает текущий статус сущности, и FSM-логика интерфейса не дрейфовала от серверной.

Что это и зачем

Доменные barrel'ы (@mineflow/eam, @mineflow/hr, @mineflow/prd, @mineflow/scm) тянут NestJS и Prisma и не собираются под web/React Native (ADR-0042). При этом фронту нужны два артефакта из домена: xstate-машины (для FSM-кнопок) и status-энумы (для лейблов и фильтров). @mineflow/api-schemas re-export'ит только эти pure-сущности из browser-safe client.ts-барреллов либ — без какой-либо backend-зависимости.

Машины здесь — те же, что исполняет бэк (ADR-0022: entity = source of truth, xstate = spec), поэтому набор доступных переходов в UI всегда совпадает с серверной валидацией. Если бэк меняет FSM, изменение приезжает сюда re-export'ом, а не копией — расхождение ловит typecheck. Пакет zero-runtime для энумов, tree-shakeable и не имеет side-effects (sideEffects: false).

Где это в слоях SDK

api-schemas — слой L0 (вместе с api-client и @mineflow/api-zod). Сами по себе машины ничего не делают — их потребляют хуки из @mineflow/client-react (useAvailableActions). Полная карта слоёв — в обзоре SDK.

Установка

pnpm add @mineflow/api-schemas
pnpm add xstate # peer-зависимость (^5.0.0)

xstate (^5.0.0) — единственная peer-зависимость. Машины — это xstate v5 state machines, поэтому xstate должен присутствовать в приложении. В монорепо MineFlow пакет резолвится как workspace:*.

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

FSM-кнопки в UI (через хук)

Базовый сценарий: показать пользователю только те действия, что разрешены текущим статусом сущности. Машину берём из api-schemas, текущий статус — из данных сущности, доступные переходы считает useAvailableActions из @mineflow/client-react.

import { assetMachine } from '@mineflow/api-schemas';
import { useAvailableActions } from '@mineflow/client-react';

function AssetActions({ asset }: { asset: { status: string } }) {
// имена FSM-событий, разрешённых из текущего статуса
const actions = useAvailableActions(assetMachine, asset.status);
// для 'operational' → ['SEND_TO_MAINTENANCE', 'CONSERVE', 'DECOMMISSION']

return (
<>
{actions.map((a) => (
<button key={a} onClick={() => runAction(a)}>
{actionLabels[a]}
</button>
))}
</>
);
}

Без хука — чистая функция

useAvailableActions — это всего лишь useMemo-обёртка над чистой функцией availableActions (тоже из client-react). Используй её вне React-дерева (например, в селекторах или утилитах):

import { assetMachine } from '@mineflow/api-schemas';
import { availableActions } from '@mineflow/client-react';

availableActions(assetMachine, 'maintenance'); // ['COMPLETE_MAINTENANCE', 'DECOMMISSION']
availableActions(assetMachine, 'decommissioned'); // [] — финальное состояние
availableActions(assetMachine, 'nonexistent'); // [] — неизвестный статус

Обе функции принимают (machine, stateValue: string) и возвращают string[] — имена переходов state-node верхнего уровня.

Status-энумы для лейблов и фильтров

Энумы — это объекты-константы со строковыми значениями, совпадающими с тем, что приходит в REST-ответах. Используй их для словарей лейблов и для типобезопасных фильтров вместо «магических строк»:

import { AssetStatus } from '@mineflow/api-schemas';

// AssetStatus.Operational === 'operational'
const assetLabels: Record<AssetStatus, string> = {
[AssetStatus.Operational]: 'В работе',
[AssetStatus.Maintenance]: 'На обслуживании',
[AssetStatus.Conserved]: 'На консервации',
[AssetStatus.Decommissioned]: 'Списан',
};
import { ShiftReportStatus } from '@mineflow/api-schemas';

// ShiftReportStatus.Submitted === 'submitted'
const onlySubmitted = reports.filter((r) => r.status === ShiftReportStatus.Submitted);
Форма энумов

Ключи — в PascalCase (AssetStatus.Operational), а значения — lowercase-строки ('operational'). Это не AssetStatus.OPERATIONAL. Часть энумов реализована как const-объект (AssetStatus), часть — как TS enum (ShiftReportStatus); со стороны потребителя обе формы используются одинаково (Enum.Member).

Ключевые экспорты

FSM-машины (xstate v5)

ДоменМашинаИмпорт
EAMassetMachineimport { assetMachine } from '@mineflow/api-schemas'
EAMmaintenanceRecordMachineimport { maintenanceRecordMachine } from '@mineflow/api-schemas'
EAMassetMovementMachineimport { assetMovementMachine } from '@mineflow/api-schemas'
HRpersonnelMachineimport { personnelMachine } from '@mineflow/api-schemas'
HRwatchAssignmentMachineimport { watchAssignmentMachine } from '@mineflow/api-schemas'
HRassetAssignmentMachineimport { assetAssignmentMachine } from '@mineflow/api-schemas'
HRtimesheetEntryMachineimport { timesheetEntryMachine } from '@mineflow/api-schemas'
HRuserAccountMachineimport { userAccountMachine } from '@mineflow/api-schemas'
PRDproductionPlanMachineimport { productionPlanMachine } from '@mineflow/api-schemas'
SCMfuelSupplyRequestMachineimport { fuelSupplyRequestMachine } from '@mineflow/api-schemas'
SCMsupplierMachineimport { supplierMachine } from '@mineflow/api-schemas'
SCMtmcInventoryMachineimport { tmcInventoryMachine } from '@mineflow/api-schemas'
SCMtmcRequestMachineimport { tmcRequestMachine } from '@mineflow/api-schemas'
SCMtmcTransferMachineimport { tmcTransferMachine } from '@mineflow/api-schemas'
У shift-reports нет xstate-машины

PRD shift-reports реализован как entity-driven FSM без отдельной xstate-машины (см. ADR-0022). Для него экспортируется только status-энум ShiftReportStatus. Для расчёта доступных действий по сменному рапорту опирайся на статус напрямую (draft → submitted → approved | rejected), а не на useAvailableActions.

Status-энумы

ДоменЭнум
EAMAssetStatus, MaintenanceStatus, AssetMovementStatus, AssetMovementOp
HRPersonnelStatus, TimesheetEntryStatus, UserAccountStatus, WatchAssignmentStatus
PRDProductionPlanStatus, ShiftReportStatus
SCMFuelSupplyRequestStatus, FuelTankDerivedStatus, SupplierStatus, TmcInventoryStatus, TmcRequestStatus, TmcTransferStatus

Тип SystemRole

import type { SystemRole } from '@mineflow/api-schemas';

Канонический RBAC-контракт — 7 системных ролей в PascalCase (CEO, Engineer, Foreman, Mechanic, Admin, OtibSpecialist, Supply). Это type-only re-export из decorator-free источника на бэке: фронт получает тип без backend-зависимости, сохраняя compile-time связь (typecheck ловит дрейф ролей). Применение ролей в UI — в рецепте RBAC.

Полный список — в API-референсе

Полный, всегда актуальный перечень экспортов с сигнатурами — в API-референсе из кода. Таблицы выше отражают состояние пакета на момент написания гайда.

Подводные камни и важные правила

warning
availableActions игнорирует guard'ы

Функция возвращает все имена переходов state-node, не учитывая xstate-guard'ы. Это сделано намеренно: UI должен предлагать действие, даже если оно требует дополнительного ввода (например, DECOMMISSION у assetMachine защищён isDoubleApproved — двойным утверждением разными людьми). Финальную валидацию перехода делает бэк и отвечает 409, если переход недопустим. Гейти UI по error.code ответа — см. рецепт Ошибки.

Имя FSM-события ≠ endpoint 1:1

availableActions(assetMachine, 'operational') вернёт 'CONSERVE', но вызывать нужно PATCH /eam/assets/:id/conserve. Маппинг «событие → REST-вызов» держат концентратные write-хуки в @mineflow/client-react (useConserveAsset, useReactivateAsset, useCompleteMaintenance, …), а не этот пакет. api-schemas отвечает только за то, какие действия показать, а не как их вызвать.

примечание
Импортируй только из @mineflow/api-schemas

Не импортируй машины из доменных barrel'ов (@mineflow/eam и т.п.) — они тянут NestJS/Prisma и не бандлятся под web/RN. Subpath @mineflow/eam/client тоже не резолвится Vite/Metro (нет exports-map у либы). Единственный поддерживаемый вход для фронта — @mineflow/api-schemas.

Status у сущности — это строка

useAvailableActions(machine, stateValue) принимает stateValue: string. Передавай поле status сущности как есть (asset.status) — значения энумов совпадают с ключами состояний машины. Для неизвестного или финального статуса вернётся [], поэтому отдельная проверка «есть ли вообще действия» сводится к actions.length > 0.

React Native

Пакет полностью browser/RN-safe — отдельной настройки под React Native не требует: машины и энумы это чистый JS без платформо-специфичных API. Единственное требование — наличие peer-зависимости xstate в RN-приложении. Хук useAvailableActions из @mineflow/client-react работает на обеих платформах одинаково. Платформо-специфика SDK (auth, fetch, random-id) живёт в других слоях — см. рецепт React Native.

Ссылки