Ваш первый модуль
Мы соберём минимальный модуль, который монтирует страницу в оболочке Building OS и читает данные через контекст платформы. Бюджет: 30 минут.
1. Создание каркаса
Начните с эталонного модуля — это канонический, заведомо рабочий образец:
# Клонируйте репозиторий платформы (или только каталог эталонного модуля)
git clone git@github.com:tangovision/tv-platform.git
cp -r tv-platform/modules/tv-module-example my-module
cd my-module2. Напишите манифест
module-manifest.json:
{
"$schema": "https://npm.k8s.tangovision.dev/@tv/extension-sdk/1.0.0/manifest.schema.json",
"id": "@acme/module-hello",
"name": "Hello Module",
"version": "1.0.0",
"minCoreVersion": ">=2.0.0",
"category": "operations",
"permissions": [
{ "subject": "building.spaces", "actions": ["read"] }
],
"events": {
"publishes": [],
"subscribes": []
},
"ui": {
"routes": [{ "path": "/hello" }],
"navigation": [
{ "label": "Hello", "path": "/hello", "section": "operations" }
]
}
}3. Проверьте его
npx tv-sdk validate module-manifest.json✓ Manifest is valid (version 1.0.0)Если он неверен, валидатор укажет точное поле:
✗ permissions[0].subject: must be one of [building.spaces, building.elements, ...]Встройте это в свой CI, чтобы сломанный манифест никогда не покидал вашу машину:
# .github/workflows/manifest.yml
- run: npx @tv/extension-sdk validate module-manifest.json4. Соберите фронтенд
Ваш модуль экспортирует федеративный Shell. Внутри него используйте контекст платформы:
// frontend/src/App.tsx
import { usePlatformContext, useBuilding } from '@tv/extension-sdk/react';
import { useQuery } from '@tanstack/react-query';
export default function App() {
const { api } = usePlatformContext();
const building = useBuilding();
const { data: spaces } = useQuery({
queryKey: ['spaces', building.id],
queryFn: () => api.get(`/api/v1/buildings/${building.id}/spaces`),
});
return (
<div>
<h1>Hello from {building.name}</h1>
<p>{spaces?.length ?? 0} spaces</p>
</div>
);
}Никаких токенов, никаких URL, которые нужно собирать вручную. Клиент api предварительно аутентифицирован и ограничен областью активного арендатора.
5. (Опционально) Объявите возможность бэкенда
Если у вашего модуля есть бэкенд на NestJS:
import { ModuleCapability, RequiresLicense } from '@tv/extension-sdk/nestjs';
@ModuleCapability({ id: 'hello.greeting', version: '1.0.0' })
@Controller('api/v1/buildings/:buildingId/hello')
export class HelloController {
@RequiresLicense('@acme/module-hello')
@Get()
greet() {
return { message: 'hello' };
}
}6. Протестируйте на мок-контексте
import { createMockPlatformContext } from '@tv/extension-sdk/testing';
import { PlatformProvider } from '@tv/extension-sdk/react';
import { render } from '@testing-library/react';
import App from './App';
const ctx = createMockPlatformContext({
user: { ...defaultUser, roles: ['manager'] },
});
render(
<PlatformProvider value={ctx}>
<App />
</PlatformProvider>,
);Вы тестируете на точно той же форме контекста, что используется в продакшене — просто заполненной фейковыми данными. Никаких сюрпризов «работает у меня, ломается в проде».
7. Запустите его в песочнице
Перед публикацией прогоните модуль на настоящем (изолированном) здании:
Что вы только что узнали
- Манифест — это ваш контракт; проверяйте его в CI.
PlatformContext— ваша единственная дверь к состоянию платформы.- Мок-контекст делает тесты соответствующими продакшену.
- Эталонный модуль — это образец для копирования.
Далее: разберитесь с PlatformContext и событиями.