Render documentation diagrams as static Mermaid assets
@@ -6,7 +6,7 @@ const props = defineProps<{
|
||||
chart: string;
|
||||
}>();
|
||||
|
||||
const svg = ref('');
|
||||
const container = ref<HTMLElement | null>(null);
|
||||
const error = ref('');
|
||||
|
||||
let initialized = false;
|
||||
@@ -20,7 +20,7 @@ function initMermaid() {
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'neutral',
|
||||
securityLevel: 'strict',
|
||||
securityLevel: 'loose',
|
||||
fontFamily: 'Inter, Arial, sans-serif',
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
@@ -34,7 +34,7 @@ function initMermaid() {
|
||||
const source = computed(() => props.chart.trim());
|
||||
|
||||
async function renderDiagram() {
|
||||
if (!import.meta.client) {
|
||||
if (!import.meta.client || !container.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,10 +42,13 @@ async function renderDiagram() {
|
||||
initMermaid();
|
||||
error.value = '';
|
||||
diagramId += 1;
|
||||
const { svg: result } = await mermaid.render(`mermaid-diagram-${diagramId}`, source.value);
|
||||
svg.value = result;
|
||||
container.value.removeAttribute('data-processed');
|
||||
container.value.id = `mermaid-diagram-${diagramId}`;
|
||||
container.value.textContent = source.value;
|
||||
await mermaid.run({
|
||||
nodes: [container.value],
|
||||
});
|
||||
} catch (cause) {
|
||||
svg.value = '';
|
||||
error.value = cause instanceof Error ? cause.message : 'Failed to render Mermaid diagram.';
|
||||
}
|
||||
}
|
||||
@@ -62,7 +65,7 @@ watch(source, () => {
|
||||
<template>
|
||||
<div class="mermaid-diagram">
|
||||
<div v-if="error" class="mermaid-diagram__error">{{ error }}</div>
|
||||
<div v-else class="mermaid-diagram__canvas" v-html="svg" />
|
||||
<pre v-else ref="container" class="mermaid-diagram__canvas mermaid" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -77,6 +80,8 @@ watch(source, () => {
|
||||
|
||||
.mermaid-diagram__canvas {
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.mermaid-diagram__canvas :deep(svg) {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
export const diagramSources: Record<string, string> = {
|
||||
'architecture-overview': `flowchart LR
|
||||
Browser[Браузер клиента]
|
||||
Frontend[web-frontend<br/>Nuxt 4 + Vue 3]
|
||||
Proxy[/api/graphql proxy]
|
||||
Backend[apollo-backend<br/>Apollo Server + Express]
|
||||
Prisma[Prisma ORM]
|
||||
Db[(PostgreSQL)]
|
||||
Vault[Vault]
|
||||
OneC[1С]
|
||||
Bots[Telegram / MAX / email]
|
||||
Dokploy[Dokploy]
|
||||
Browser["Браузер клиента"]
|
||||
Frontend["web-frontend<br/>Nuxt 4 + Vue 3"]
|
||||
Proxy["/api/graphql proxy"]
|
||||
Backend["apollo-backend<br/>Apollo Server + Express"]
|
||||
Prisma["Prisma ORM"]
|
||||
Db[("PostgreSQL")]
|
||||
Vault["Vault"]
|
||||
OneC["1С"]
|
||||
Bots["Telegram / MAX / email"]
|
||||
Dokploy["Dokploy"]
|
||||
|
||||
Browser --> Frontend
|
||||
Frontend --> Proxy
|
||||
@@ -22,32 +22,32 @@ export const diagramSources: Record<string, string> = {
|
||||
Frontend -.deploy.-> Dokploy
|
||||
Backend -.deploy.-> Dokploy`,
|
||||
'component-map': `flowchart LR
|
||||
subgraph UI[UI и маршруты]
|
||||
Pages[app/pages]
|
||||
Components[app/components]
|
||||
Composables[app/composables]
|
||||
Middleware[middleware / plugins]
|
||||
subgraph UI["UI и маршруты"]
|
||||
Pages["app/pages"]
|
||||
Components["app/components"]
|
||||
Composables["app/composables"]
|
||||
Middleware["middleware / plugins"]
|
||||
end
|
||||
|
||||
subgraph Transport[Контракт и transport]
|
||||
Ops[graphql/operations]
|
||||
Generated[generated.ts]
|
||||
Apollo[Apollo client]
|
||||
Api[server/api/graphql]
|
||||
subgraph Transport["Контракт и transport"]
|
||||
Ops["graphql/operations"]
|
||||
Generated["generated.ts"]
|
||||
Apollo["Apollo client"]
|
||||
Api["server/api/graphql"]
|
||||
end
|
||||
|
||||
subgraph Server[Backend logic]
|
||||
Schema[schema.graphql]
|
||||
Resolvers[resolvers.js]
|
||||
Auth[auth.js / access.js]
|
||||
Context[context.js]
|
||||
Messenger[messenger / telegram / max]
|
||||
subgraph Server["Backend logic"]
|
||||
Schema["schema.graphql"]
|
||||
Resolvers["resolvers.js"]
|
||||
Auth["auth.js / access.js"]
|
||||
Context["context.js"]
|
||||
Messenger["messenger / telegram / max"]
|
||||
end
|
||||
|
||||
subgraph Data[Data layer]
|
||||
Prisma[prisma-client.js]
|
||||
PrismaSchema[schema.prisma]
|
||||
Db[(PostgreSQL)]
|
||||
subgraph Data["Data layer"]
|
||||
Prisma["prisma-client.js"]
|
||||
PrismaSchema["schema.prisma"]
|
||||
Db[("PostgreSQL")]
|
||||
end
|
||||
|
||||
Pages --> Components
|
||||
@@ -67,33 +67,33 @@ export const diagramSources: Record<string, string> = {
|
||||
Prisma --> PrismaSchema
|
||||
Prisma --> Db`,
|
||||
'infrastructure-topology': `flowchart TB
|
||||
subgraph Repo[Репозиторий fregat]
|
||||
FrontendRepo[web-frontend]
|
||||
BackendRepo[apollo-backend]
|
||||
TgRepo[tg-bot]
|
||||
MaxRepo[max-bot]
|
||||
BonusRepo[bonus-bot]
|
||||
WorkerRepo[hatchet-worker]
|
||||
VaultRepo[vault]
|
||||
subgraph Repo["Репозиторий fregat"]
|
||||
FrontendRepo["web-frontend"]
|
||||
BackendRepo["apollo-backend"]
|
||||
TgRepo["tg-bot"]
|
||||
MaxRepo["max-bot"]
|
||||
BonusRepo["bonus-bot"]
|
||||
WorkerRepo["hatchet-worker"]
|
||||
VaultRepo["vault"]
|
||||
end
|
||||
|
||||
subgraph Dokploy[Dokploy]
|
||||
FrontendSvc[web-frontend service]
|
||||
BackendSvc[apollo-backend service]
|
||||
TgSvc[tg-bot service]
|
||||
MaxSvc[max-bot service]
|
||||
BonusSvc[bonus-bot service]
|
||||
WorkerSvc[hatchet-worker service]
|
||||
VaultSvc[vault service]
|
||||
subgraph Dokploy["Dokploy"]
|
||||
FrontendSvc["web-frontend service"]
|
||||
BackendSvc["apollo-backend service"]
|
||||
TgSvc["tg-bot service"]
|
||||
MaxSvc["max-bot service"]
|
||||
BonusSvc["bonus-bot service"]
|
||||
WorkerSvc["hatchet-worker service"]
|
||||
VaultSvc["vault service"]
|
||||
end
|
||||
|
||||
subgraph Infra[Инфраструктурный контур]
|
||||
VaultData[Vault raft storage /vault/data]
|
||||
BackendDb[(PostgreSQL for app)]
|
||||
HatchetEngine[Hatchet engine]
|
||||
HatchetPg[(PostgreSQL for Hatchet)]
|
||||
OneC[1С]
|
||||
Tailscale[Tailscale SSH / diagnostics]
|
||||
subgraph Infra["Инфраструктурный контур"]
|
||||
VaultData["Vault raft storage /vault/data"]
|
||||
BackendDb[("PostgreSQL for app")]
|
||||
HatchetEngine["Hatchet engine"]
|
||||
HatchetPg[("PostgreSQL for Hatchet")]
|
||||
OneC["1С"]
|
||||
Tailscale["Tailscale SSH / diagnostics"]
|
||||
end
|
||||
|
||||
FrontendRepo --> FrontendSvc
|
||||
@@ -160,78 +160,78 @@ export const diagramSources: Record<string, string> = {
|
||||
User "1" --> "*" ReferralLink : referrals
|
||||
CatalogProductTypeSetting --> Product : productType`,
|
||||
dashboard: `flowchart TB
|
||||
subgraph Page[Главная страница клиента]
|
||||
Header[Header / навигация]
|
||||
Quick[Быстрые действия]
|
||||
Active[Актуальные заказы и заявки]
|
||||
Notes[Последние уведомления]
|
||||
Bonus[Бонусный блок]
|
||||
subgraph Page["Главная страница клиента"]
|
||||
Header["Header / навигация"]
|
||||
Quick["Быстрые действия"]
|
||||
Active["Актуальные заказы и заявки"]
|
||||
Notes["Последние уведомления"]
|
||||
Bonus["Бонусный блок"]
|
||||
end
|
||||
|
||||
Header --> Quick --> Active --> Notes --> Bonus`,
|
||||
'catalog-grid': `flowchart TB
|
||||
subgraph Catalog[Каталог продукции]
|
||||
Title[Заголовок раздела]
|
||||
Search[Поиск]
|
||||
Grid[Сетка карточек товарных направлений]
|
||||
Cards[Упаковочный скотч | Алюминиевый скотч | Крепп | Вспененный | Двусторонний ПП | Двусторонний PVC]
|
||||
subgraph Catalog["Каталог продукции"]
|
||||
Title["Заголовок раздела"]
|
||||
Search["Поиск"]
|
||||
Grid["Сетка карточек товарных направлений"]
|
||||
Cards["Упаковочный скотч | Алюминиевый скотч | Крепп | Вспененный | Двусторонний ПП | Двусторонний PVC"]
|
||||
end
|
||||
|
||||
Title --> Search --> Grid --> Cards`,
|
||||
'product-card': `flowchart TB
|
||||
subgraph ProductPage[Карточка товара]
|
||||
Title[Заголовок товара и навигация]
|
||||
subgraph TopRow[Верхний блок]
|
||||
Image[Изображение товара]
|
||||
Params[Параметры выбора]
|
||||
Action[SKU / действие В корзину]
|
||||
subgraph ProductPage["Карточка товара"]
|
||||
Title["Заголовок товара и навигация"]
|
||||
subgraph TopRow["Верхний блок"]
|
||||
Image["Изображение товара"]
|
||||
Params["Параметры выбора"]
|
||||
Action["SKU / действие В корзину"]
|
||||
end
|
||||
Help[Пояснения по параметрам]
|
||||
Table[Таблица доступных вариантов]
|
||||
Help["Пояснения по параметрам"]
|
||||
Table["Таблица доступных вариантов"]
|
||||
end
|
||||
|
||||
Title --> TopRow --> Help --> Table`,
|
||||
cart: `flowchart TB
|
||||
subgraph CartPage[Корзина]
|
||||
Items[Список выбранных позиций]
|
||||
Delivery[Адрес доставки]
|
||||
Comment[Комментарий клиента]
|
||||
Submit[Отправить заявку]
|
||||
subgraph CartPage["Корзина"]
|
||||
Items["Список выбранных позиций"]
|
||||
Delivery["Адрес доставки"]
|
||||
Comment["Комментарий клиента"]
|
||||
Submit["Отправить заявку"]
|
||||
end
|
||||
|
||||
Items --> Delivery --> Comment --> Submit`,
|
||||
'client-order': `flowchart TB
|
||||
subgraph ClientOrder[Карточка заявки / заказа]
|
||||
Summary[Номер документа и статус]
|
||||
Composition[Состав позиций]
|
||||
Terms[Стоимость и условия поставки]
|
||||
History[История изменений]
|
||||
subgraph ClientOrder["Карточка заявки / заказа"]
|
||||
Summary["Номер документа и статус"]
|
||||
Composition["Состав позиций"]
|
||||
Terms["Стоимость и условия поставки"]
|
||||
History["История изменений"]
|
||||
end
|
||||
|
||||
Summary --> Composition --> Terms --> History`,
|
||||
'bonus-cabinet': `flowchart TB
|
||||
subgraph BonusPage[Бонусный кабинет]
|
||||
Balance[Текущий бонусный баланс]
|
||||
History[История операций]
|
||||
Referrals[Реферальные связи]
|
||||
Action[Подача заявки на использование или вывод]
|
||||
subgraph BonusPage["Бонусный кабинет"]
|
||||
Balance["Текущий бонусный баланс"]
|
||||
History["История операций"]
|
||||
Referrals["Реферальные связи"]
|
||||
Action["Подача заявки на использование или вывод"]
|
||||
end
|
||||
|
||||
Balance --> History --> Referrals --> Action`,
|
||||
'manager-order': `flowchart TB
|
||||
subgraph ManagerOrder[Карточка обработки заявки]
|
||||
Summary[Клиент / контрагент / менеджер]
|
||||
Payload[Состав заявки или расчетный payload]
|
||||
Terms[Стоимость и условия поставки]
|
||||
Actions[Опубликовать условия / перевести в работу / отменить]
|
||||
subgraph ManagerOrder["Карточка обработки заявки"]
|
||||
Summary["Клиент / контрагент / менеджер"]
|
||||
Payload["Состав заявки или расчетный payload"]
|
||||
Terms["Стоимость и условия поставки"]
|
||||
Actions["Опубликовать условия / перевести в работу / отменить"]
|
||||
end
|
||||
|
||||
Summary --> Payload --> Terms --> Actions`,
|
||||
'manager-orders': `flowchart TB
|
||||
subgraph ManagerOrders[Список заказов менеджера]
|
||||
Header[Заголовок раздела]
|
||||
Filters[Фильтры: статус / клиент / период]
|
||||
Table[Таблица заказов]
|
||||
subgraph ManagerOrders["Список заказов менеджера"]
|
||||
Header["Заголовок раздела"]
|
||||
Filters["Фильтры: статус / клиент / период"]
|
||||
Table["Таблица заказов"]
|
||||
end
|
||||
|
||||
Header --> Filters --> Table`,
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import DefaultTheme from 'vitepress/theme';
|
||||
import type { Theme } from 'vitepress';
|
||||
|
||||
import MermaidDiagram from './components/MermaidDiagram.vue';
|
||||
import NamedMermaidDiagram from './components/NamedMermaidDiagram.vue';
|
||||
|
||||
const theme: Theme = {
|
||||
...DefaultTheme,
|
||||
enhanceApp({ app }) {
|
||||
DefaultTheme.enhanceApp?.({ app });
|
||||
app.component('MermaidDiagram', MermaidDiagram);
|
||||
app.component('NamedMermaidDiagram', NamedMermaidDiagram);
|
||||
enhanceApp(ctx) {
|
||||
DefaultTheme.enhanceApp?.(ctx);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
1
docs/public/diagrams/architecture-overview.svg
Normal file
|
After Width: | Height: | Size: 25 KiB |
1
docs/public/diagrams/component-map.svg
Normal file
|
After Width: | Height: | Size: 36 KiB |
1
docs/public/diagrams/database-model.svg
Normal file
|
After Width: | Height: | Size: 86 KiB |
1
docs/public/diagrams/infrastructure-topology.svg
Normal file
|
After Width: | Height: | Size: 44 KiB |
1
docs/public/prototypes/bonus-cabinet.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
1
docs/public/prototypes/cart.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
1
docs/public/prototypes/catalog-grid.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
1
docs/public/prototypes/client-order.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
1
docs/public/prototypes/dashboard.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
1
docs/public/prototypes/manager-order.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
1
docs/public/prototypes/manager-orders.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
docs/public/prototypes/product-card.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
9
docs/scripts/mermaid-config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"theme": "neutral",
|
||||
"securityLevel": "loose",
|
||||
"flowchart": {
|
||||
"useMaxWidth": true,
|
||||
"htmlLabels": true
|
||||
},
|
||||
"fontFamily": "Inter, Arial, sans-serif"
|
||||
}
|
||||
9
docs/scripts/puppeteer-config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"headless": true,
|
||||
"executablePath": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"args": [
|
||||
"--no-sandbox",
|
||||
"--disable-setuid-sandbox",
|
||||
"--disable-dev-shm-usage"
|
||||
]
|
||||
}
|
||||
80
docs/scripts/render-mermaid-assets.mjs
Normal file
@@ -0,0 +1,80 @@
|
||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { diagramSources } from '../.vitepress/theme/components/diagramSources.ts';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const docsRoot = path.resolve(__dirname, '..');
|
||||
const publicRoot = path.join(docsRoot, 'public');
|
||||
const diagramsDir = path.join(publicRoot, 'diagrams');
|
||||
const prototypesDir = path.join(publicRoot, 'prototypes');
|
||||
const puppeteerConfig = path.join(__dirname, 'puppeteer-config.json');
|
||||
const mermaidConfig = path.join(__dirname, 'mermaid-config.json');
|
||||
|
||||
const prototypeNames = new Set([
|
||||
'dashboard',
|
||||
'catalog-grid',
|
||||
'product-card',
|
||||
'cart',
|
||||
'client-order',
|
||||
'bonus-cabinet',
|
||||
'manager-order',
|
||||
'manager-orders',
|
||||
]);
|
||||
|
||||
function runCommand(command, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(command, args, {
|
||||
cwd: docsRoot,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
child.on('exit', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
reject(new Error(`${command} ${args.join(' ')} exited with code ${code ?? 'unknown'}`));
|
||||
});
|
||||
|
||||
child.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function renderDiagram(name, source) {
|
||||
const targetDir = prototypeNames.has(name) ? prototypesDir : diagramsDir;
|
||||
const tmpDir = await mkdtemp(path.join(os.tmpdir(), `mermaid-${name}-`));
|
||||
const inputFile = path.join(tmpDir, `${name}.mmd`);
|
||||
const outputFile = path.join(targetDir, `${name}.svg`);
|
||||
|
||||
try {
|
||||
await writeFile(inputFile, source, 'utf8');
|
||||
await runCommand('pnpm', [
|
||||
'exec',
|
||||
'mmdc',
|
||||
'-i',
|
||||
inputFile,
|
||||
'-o',
|
||||
outputFile,
|
||||
'-b',
|
||||
'transparent',
|
||||
'-c',
|
||||
mermaidConfig,
|
||||
'-p',
|
||||
puppeteerConfig,
|
||||
]);
|
||||
} finally {
|
||||
await rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
await mkdir(diagramsDir, { recursive: true });
|
||||
await mkdir(prototypesDir, { recursive: true });
|
||||
|
||||
for (const [name, source] of Object.entries(diagramSources)) {
|
||||
await renderDiagram(name, source);
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Основное хранилище данных программного продукта реализуется на `PostgreSQL`. Прикладной доступ к данным осуществляется через `Prisma ORM`.
|
||||
|
||||
<NamedMermaidDiagram name="database-model" />
|
||||

|
||||
|
||||
Модель данных должна обеспечивать хранение:
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="dashboard" />
|
||||

|
||||
|
||||
### 10.3.2 Каталог продукции
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="catalog-grid" />
|
||||

|
||||
|
||||
### 10.3.3 Карточка товара
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="product-card" />
|
||||

|
||||
|
||||
Состав блока выбора параметров:
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="cart" />
|
||||

|
||||
|
||||
### 10.3.5 Карточка заявки или заказа
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="client-order" />
|
||||

|
||||
|
||||
Состав страницы:
|
||||
|
||||
@@ -221,7 +221,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="bonus-cabinet" />
|
||||

|
||||
|
||||
## 10.4 Менеджерские экранные формы
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="manager-order" />
|
||||

|
||||
|
||||
Состав страницы:
|
||||
|
||||
@@ -288,7 +288,7 @@
|
||||
|
||||
Схематичный прототип:
|
||||
|
||||
<NamedMermaidDiagram name="manager-orders" />
|
||||

|
||||
|
||||
### 10.4.5 Настройки каталога
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Программный продукт реализуется по клиент-серверной модели и включает веб-клиент, сервер бизнес-логики, базу данных, модуль интеграции и вспомогательные сервисы уведомлений.
|
||||
|
||||
<NamedMermaidDiagram name="architecture-overview" />
|
||||

|
||||
|
||||
## 7.2 Состав прикладных сервисов
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
## 7.6 Карта слоев и компонентов
|
||||
|
||||
<NamedMermaidDiagram name="component-map" />
|
||||

|
||||
|
||||
## 7.7 Архитектура серверной части
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
|
||||
Текущая инфраструктурная схема проекта включает прикладные сервисы, сервис секретов, мессенджерные сервисы и вспомогательный worker-контур.
|
||||
|
||||
<NamedMermaidDiagram name="infrastructure-topology" />
|
||||

|
||||
|
||||
Сервисы проекта и способ их развёртывания:
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"@graphql-codegen/typescript": "^5.0.9",
|
||||
"@graphql-codegen/typescript-operations": "^5.0.9",
|
||||
"@graphql-codegen/typescript-vue-apollo": "^5.0.0",
|
||||
"@mermaid-js/mermaid-cli": "^11.14.0",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/vue3-vite": "^8.6.14",
|
||||
"storybook": "^8.6.14",
|
||||
|
||||