diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..34327d5 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,75 @@ +name: Documentation + +on: + push: + branches: [master] + paths: + - '.github/workflows/docs.yml' + - 'docs/**' + - 'site/**' + - 'scripts/check-docs.mjs' + - 'package.json' + - 'package-lock.json' + pull_request: + paths: + - '.github/workflows/docs.yml' + - 'docs/**' + - 'site/**' + - 'scripts/check-docs.mjs' + - 'package.json' + - 'package-lock.json' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Check specification + run: npm run check:docs + + - name: Build documentation + run: npm run docs:build + + - name: Check documentation search + run: npm run check:docs-search + + - name: Configure Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site/.vitepress/dist + + deploy: + if: github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 2752eb9..6e3f5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules/ +site/.vitepress/cache/ +site/.vitepress/dist/ .DS_Store diff --git a/README.md b/README.md index 05e6921..3683319 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ ## Структура -- `docs/` — исходная документация и спецификация SLM Design. +- `docs/` — новый нормативный корпус и материалы сайта. +- `old-docs/` — действующая legacy-документация для текущего skill. +- `site/` — VitePress-конфигурация, тема и статические ресурсы. - `src-skills/` — исходники agent skills. - `skills/` — собранные skills для установки через `npx skills`. @@ -17,7 +19,7 @@ npm run build npm run check ``` -`npm run build` пересобирает `skills/slm-design/` из `docs/` и `src-skills/slm-design/`. Не редактируй собранные файлы вручную. +`npm run build` пересобирает текущий `skills/slm-design/` из `old-docs/` и `src-skills/slm-design/`. Не редактируй собранные файлы вручную. ## Установка diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..9b292be --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,24 @@ +--- +layout: home +title: SLM Design in English +titleTemplate: false +sidebar: false +hero: + name: English edition + text: Translation is planned + tagline: The Russian specification is currently the only normative source. The English edition will preserve its structure and rule IDs. + actions: + - theme: brand + text: Open Russian specification + link: /ru/specification/ + - theme: alt + text: Language selection + link: / +features: + - title: No partial translation + details: An incomplete English rule set is not published as normative documentation. + - title: Stable identifiers + details: Future translated requirements will use the same SLM rule IDs as the Russian source. + - title: Equal URL structure + details: English documentation is reserved under /en/ alongside the Russian /ru/ section. +--- diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..49efd43 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,24 @@ +--- +layout: home +title: SLM Design +titleTemplate: false +sidebar: false +hero: + name: SLM Design + text: Explicit architecture boundaries + tagline: A draft specification for ownership, dependencies, runtime, and lifecycle in product applications. + actions: + - theme: brand + text: Русская спецификация + link: /ru/ + - theme: alt + text: English + link: /en/ +features: + - title: Base SLM + details: A complete minimal architecture built around explicit ownership and five application layers. + - title: Independent overlays + details: Advanced and Pro add separate rule sets directly to base SLM without inheriting each other. + - title: Stable rules + details: Every normative requirement has a permanent rule ID suitable for reviews and automated checks. +--- diff --git a/docs/ru/index.md b/docs/ru/index.md new file mode 100644 index 0000000..35e4b08 --- /dev/null +++ b/docs/ru/index.md @@ -0,0 +1,28 @@ +--- +layout: home +title: SLM Design +titleTemplate: false +sidebar: false +hero: + name: Документация SLM Design + text: Один портал для правил и практики + tagline: Нормативная спецификация, реестр правил и будущий архитектурный гайд в единой структуре. + actions: + - theme: brand + text: Открыть спецификацию + link: /ru/specification/ + - theme: alt + text: Найти правило + link: /ru/specification/rules +features: + - title: SLM Design Specification + details: Нормативный источник архитектурных правил, ownership boundaries и требований соответствия. + link: /ru/specification/ + linkText: Читать спецификацию + - title: Реестр правил + details: Все Base, Advanced и Pro rules с фильтрами, точными anchors и копируемыми permalink-ссылками. + link: /ru/specification/rules + linkText: Открыть реестр + - title: Architecture Guide + details: Будущий учебный материал для последовательного изучения и практического применения Specification. +--- diff --git a/docs2/specification/architecture-model.md b/docs/ru/specification/architecture-model.md similarity index 62% rename from docs2/specification/architecture-model.md rename to docs/ru/specification/architecture-model.md index dce88b7..ca43821 100644 --- a/docs2/specification/architecture-model.md +++ b/docs/ru/specification/architecture-model.md @@ -17,7 +17,7 @@ src/ └── shared/ ``` -**SLM-ARCH-001 - ОБЯЗАН.** Base SLM-приложение должно разделять код по ответственности между слоями `app`, `compositions`, `infra`, `ui` и `shared`. +**SLM-BASE-ARCH-001 - ОБЯЗАН.** Base SLM-приложение должно разделять код по ответственности между слоями `app`, `compositions`, `infra`, `ui` и `shared`. Не каждый слой обязан содержать код в минимальном приложении. Пустые папки и speculative scaffolding не требуются. @@ -42,11 +42,11 @@ shared -/-> остальные SLM-слои Framework APIs и external packages регулируются ответственностью импортирующего слоя и не показаны как SLM-слои. -**SLM-ARCH-002 - ОБЯЗАН.** Верхнеуровневое направление зависимостей между base SLM-слоями должно соблюдаться для runtime imports и type imports, кроме явно описанных исключений. +**SLM-BASE-ARCH-002 - ОБЯЗАН.** Верхнеуровневое направление зависимостей между base SLM-слоями должно соблюдаться для runtime imports и type imports, кроме явно описанных исключений. -**SLM-ARCH-003 - ЗАПРЕЩЕНО.** Нижний слой не может импортировать `app` или `compositions`. +**SLM-BASE-ARCH-003 - ЗАПРЕЩЕНО.** Нижний слой не может импортировать `app` или `compositions`. -**SLM-ARCH-004 - ЗАПРЕЩЕНО.** `infra`, `ui` и `shared` не могут владеть product wiring или выступать service locator для application modules. +**SLM-BASE-ARCH-004 - ЗАПРЕЩЕНО.** `infra`, `ui` и `shared` не могут владеть product wiring или выступать service locator для application modules. ## Путь данных @@ -57,7 +57,7 @@ app -> external source ``` -**SLM-ARCH-005 - ОБЯЗАН.** Каждый переход product data должен сохранять ownership: framework связывает, product owner определяет semantics, а technical capability не присваивает себе product model. +**SLM-BASE-ARCH-005 - ОБЯЗАН.** Каждый переход product data должен сохранять ownership: framework связывает, product owner определяет semantics, а technical capability не присваивает себе product model. ## Путь UI diff --git a/docs2/specification/architecture-modes.md b/docs/ru/specification/architecture-modes.md similarity index 52% rename from docs2/specification/architecture-modes.md rename to docs/ru/specification/architecture-modes.md index 2138092..4498e46 100644 --- a/docs2/specification/architecture-modes.md +++ b/docs/ru/specification/architecture-modes.md @@ -25,11 +25,11 @@ SLM + Advanced SLM + Pro ``` -**SLM-MODE-001 - ОБЯЗАН.** Приложение должно зафиксировать использование base SLM и, при наличии, ровно одного overlay: `Advanced` или `Pro`. +**SLM-BASE-MODE-001 - ОБЯЗАН.** Приложение должно зафиксировать использование base SLM и, при наличии, ровно одного overlay: `Advanced` или `Pro`. -**SLM-MODE-002 - ЗАПРЕЩЕНО.** Одно приложение не может одновременно заявлять соответствие `SLM Advanced` и `SLM Pro`. +**SLM-BASE-MODE-002 - ЗАПРЕЩЕНО.** Одно приложение не может одновременно заявлять соответствие `SLM Advanced` и `SLM Pro`. -**SLM-MODE-003 - ОБЯЗАН.** Выбранный overlay должен применяться ко всему приложению в пределах одной SLM application boundary. +**SLM-BASE-MODE-003 - ОБЯЗАН.** Выбранный overlay должен применяться ко всему приложению в пределах одной SLM application boundary. Выбор выполняет команда на стадии планирования. Сигналами могут быть количество product responsibilities, связанность modules, runtime state, client/server execution, lifecycle risks и количество команд разработки. Фиксированные числовые пороги не устанавливаются. @@ -44,7 +44,7 @@ SLM + Pro Base-правило имеет идентификатор вида: ```text -SLM-AREA-NNN +SLM-BASE-AREA-NNN ``` Mode-specific правила имеют идентификаторы: @@ -54,15 +54,15 @@ SLM-ADV-AREA-NNN SLM-PRO-AREA-NNN ``` -**SLM-MODE-004 - ОБЯЗАН.** Base-правила SLM применяются при любом выбранном варианте архитектуры. Если overlay явно заменяет base rule только в определённом scope, исходное base-правило продолжает действовать за пределами этого scope. +**SLM-BASE-MODE-004 - ОБЯЗАН.** Base-правила SLM применяются при любом выбранном варианте архитектуры. Если overlay явно заменяет base rule только в определённом scope, исходное base-правило продолжает действовать за пределами этого scope. -**SLM-MODE-005 - ОБЯЗАН.** Для `SLM Advanced` применяются только base-правила и правила из `modes/advanced`. +**SLM-BASE-MODE-005 - ОБЯЗАН.** Для `SLM Advanced` применяются только base-правила и правила из `modes/advanced`. -**SLM-MODE-006 - ОБЯЗАН.** Для `SLM Pro` применяются только base-правила и правила из `modes/pro`. +**SLM-BASE-MODE-006 - ОБЯЗАН.** Для `SLM Pro` применяются только base-правила и правила из `modes/pro`. -**SLM-MODE-007 - ЗАПРЕЩЕНО.** Правило другого overlay не может использоваться как обязательное требование, разрешение или исключение. +**SLM-BASE-MODE-007 - ЗАПРЕЩЕНО.** Правило другого overlay не может использоваться как обязательное требование, разрешение или исключение. -**SLM-MODE-008 - ОБЯЗАН.** Mode-specific правило, заменяющее base-поведение, должно явно назвать заменяемый base rule ID или нормативный раздел и точный scope замены. +**SLM-BASE-MODE-008 - ОБЯЗАН.** Mode-specific правило, заменяющее base-поведение, должно явно назвать заменяемый base rule ID или нормативный раздел и точный scope замены. ## Независимые overlays @@ -76,6 +76,6 @@ SLM-PRO-AREA-NNN ## Изменение overlay -**SLM-MODE-009 - МОЖЕТ.** Команда может подключить, заменить или удалить overlay при изменении требований к архитектуре. +**SLM-BASE-MODE-009 - МОЖЕТ.** Команда может подключить, заменить или удалить overlay при изменении требований к архитектуре. -**SLM-MODE-010 - ОБЯЗАН.** После изменения конфигурации приложение может заявлять соответствие только после выполнения применимых base-правил с учётом scoped replacements и, при наличии, полного rule set выбранного overlay. +**SLM-BASE-MODE-010 - ОБЯЗАН.** После изменения конфигурации приложение может заявлять соответствие только после выполнения применимых base-правил с учётом scoped replacements и, при наличии, полного rule set выбранного overlay. diff --git a/docs/ru/specification/foundations.md b/docs/ru/specification/foundations.md new file mode 100644 index 0000000..1e59051 --- /dev/null +++ b/docs/ru/specification/foundations.md @@ -0,0 +1,39 @@ +--- +title: Основные инварианты +status: draft +normative: true +--- + +# Основные Инварианты + +SLM Design организует frontend-приложение по владельцам ответственности. Архитектурная единица определяется не типом файла, а тем, кто владеет моделью, поведением, данными, runtime и lifecycle. + +## Ответственность до размещения + +**SLM-BASE-FND-001 - ОБЯЗАН.** Перед размещением кода необходимо определить его владельца, public boundary, runtime dependencies и lifecycle scope. + +**SLM-BASE-FND-002 - СЛЕДУЕТ.** Код следует размещать в минимальном scope, который полностью владеет его ответственностью. + +**SLM-BASE-FND-003 - ЗАПРЕЩЕНО.** Нельзя переносить код в общий слой или общий package только на основании предполагаемого будущего переиспользования. + +## Путь продуктовых данных + +Product data проходят через public boundary текущего владельца согласно [SLM-BASE-DATA-001](./state-and-data.md#product-gateway). Внешний сервис может оставаться физическим источником данных, но transport contract не становится product model автоматически. + +## Явные зависимости + +**SLM-BASE-FND-007 - ОБЯЗАН.** Runtime capabilities должны поступать владельцу поведения через разрешённые imports, явные arguments или contracts, а не через скрытый service locator или global mutable state. + +**SLM-BASE-FND-008 - ЗАПРЕЩЕНО.** Type cast, barrel, alias, dynamic import или helper в `shared` не могут использоваться для обхода применимой архитектурной границы. + +## Public API + +Межмодульное взаимодействие и deep imports регулируются [SLM-BASE-API-001 - SLM-BASE-API-005](./public-api-and-imports.md#общие-правила). + +## Scope и lifecycle + +Создание, scope, activation и cleanup применимых runtimes и resources определены в [Runtime и lifecycle](./runtime-and-lifecycle.md). + +## Overlays + +Base SLM не вводит дополнительные архитектурные слои и специализированные runtime contracts. Каждый overlay самостоятельно определяет свои добавления и замены base-правил. diff --git a/docs2/specification/index.md b/docs/ru/specification/index.md similarity index 76% rename from docs2/specification/index.md rename to docs/ru/specification/index.md index 0a1c6dc..b73d420 100644 --- a/docs2/specification/index.md +++ b/docs/ru/specification/index.md @@ -9,7 +9,7 @@ normative: true Эта директория содержит единый нормативный корпус SLM Design 2.0. Base SLM является законченной минимальной архитектурой; дополнительные ограничения подключаются независимыми overlays `SLM Advanced` или `SLM Pro`. -Пока статус равен `draft`, документы описывают проектируемую архитектуру и не заменяют действующую документацию в `docs/`. +Пока статус равен `draft`, документы описывают проектируемую архитектуру и не заменяют действующую документацию в `old-docs/`. ## Нормативный язык @@ -20,7 +20,9 @@ normative: true | `СЛЕДУЕТ` | Рекомендуемое решение; отступление требует явного обоснования | | `МОЖЕТ` | Допустимый, но необязательный вариант | -Правила имеют стабильные идентификаторы. Base использует формат `SLM-AREA-NNN`, Advanced - `SLM-ADV-AREA-NNN`, Pro - `SLM-PRO-AREA-NNN`. Точное нормативное требование принадлежит только той главе, где объявлен его rule ID. +Правила имеют стабильные идентификаторы. Base использует формат `SLM-BASE-AREA-NNN`, Advanced - `SLM-ADV-AREA-NNN`, Pro - `SLM-PRO-AREA-NNN`. Точное нормативное требование принадлежит только той главе, где объявлен его rule ID. + +Все объявления доступны в [реестре правил](./rules.md). Для прямого перехода можно открыть поиск `Ctrl/⌘ K` и ввести полный rule ID. ## Architecture modes @@ -33,11 +35,11 @@ SLM Pro = SLM + Pro rules ## Приоритет -**SLM-DOC-001 - ОБЯЗАН.** При конфликте между главами спецификации и любым ненормативным материалом приоритет имеет спецификация. +**SLM-BASE-DOC-001 - ОБЯЗАН.** При конфликте между главами спецификации и любым ненормативным материалом приоритет имеет спецификация. -**SLM-DOC-002 - ЗАПРЕЩЕНО.** Ненормативный документ не может вводить новое обязательное правило, исключение или архитектурную границу. +**SLM-BASE-DOC-002 - ЗАПРЕЩЕНО.** Ненормативный документ не может вводить новое обязательное правило, исключение или архитектурную границу. -**SLM-DOC-003 - ОБЯЗАН.** Изменение принятого архитектурного правила должно вноситься в главу, которая владеет соответствующим rule ID. +**SLM-BASE-DOC-003 - ОБЯЗАН.** Изменение принятого архитектурного правила должно вноситься в главу, которая владеет соответствующим rule ID. ## Base SLM diff --git a/docs2/specification/layers/app.md b/docs/ru/specification/layers/app.md similarity index 53% rename from docs2/specification/layers/app.md rename to docs/ru/specification/layers/app.md index f7c34ec..2e730a8 100644 --- a/docs2/specification/layers/app.md +++ b/docs/ru/specification/layers/app.md @@ -21,26 +21,26 @@ normative: true ## Правила -**SLM-APP-001 - ОБЯЗАН.** Route entry должен оставаться тонким adapter, нормализующим framework input и делегирующим готовому composition module. +**SLM-BASE-APP-001 - ОБЯЗАН.** Route entry должен оставаться тонким adapter, нормализующим framework input и делегирующим готовому composition module. ```text framework route → composition entry ``` -**SLM-APP-002 - ЗАПРЕЩЕНО.** `app` не может владеть product page, screen, widget, product scenario, store или application wiring. +**SLM-BASE-APP-002 - ЗАПРЕЩЕНО.** `app` не может владеть product page, screen, widget, product scenario, store или application wiring. -**SLM-APP-003 - ЗАПРЕЩЕНО.** Route entry не должен напрямую собирать product integrations, вызывать SDK или формировать product model. +**SLM-BASE-APP-003 - ЗАПРЕЩЕНО.** Route entry не должен напрямую собирать product integrations, вызывать SDK или формировать product model. -**SLM-APP-004 - ОБЯЗАН.** Framework-specific input должен быть считан в `app` и передан вниз в минимальной нормализованной форме. +**SLM-BASE-APP-004 - ОБЯЗАН.** Framework-specific input должен быть считан в `app` и передан вниз в минимальной нормализованной форме. Механическая нормализация включает извлечение route params, headers и framework wrappers. Product validation, создание value objects и выбор product outcome остаются у владельца product semantics. -Запрет другим SLM-слоям импортировать `app` определяется base-правилом `SLM-ARCH-003`. +Запрет другим SLM-слоям импортировать `app` определяется base-правилом `SLM-BASE-ARCH-003`. -**SLM-APP-006 - СЛЕДУЕТ.** Framework behavior, которому нужны product dependencies или product UI, следует реализовать готовым composition entry и только подключить из `app`. +**SLM-BASE-APP-006 - СЛЕДУЕТ.** Framework behavior, которому нужны product dependencies или product UI, следует реализовать готовым composition entry и только подключить из `app`. -**SLM-APP-007 - МОЖЕТ.** `app` может напрямую импортировать framework APIs и static/global resources из `shared`, если framework требует подключить их в root entry. +**SLM-BASE-APP-007 - МОЖЕТ.** `app` может напрямую импортировать framework APIs и static/global resources из `shared`, если framework требует подключить их в root entry. ## Допустимая структура @@ -59,7 +59,7 @@ app/ ## Примеры нарушений -Следующие сущности являются примерами нарушений `SLM-APP-002` и `SLM-APP-003`: +Следующие сущности являются примерами нарушений `SLM-BASE-APP-002` и `SLM-BASE-APP-003`: - `ProductPage`; - product Provider; diff --git a/docs2/specification/layers/compositions.md b/docs/ru/specification/layers/compositions.md similarity index 52% rename from docs2/specification/layers/compositions.md rename to docs/ru/specification/layers/compositions.md index a871af2..b3b40f2 100644 --- a/docs2/specification/layers/compositions.md +++ b/docs/ru/specification/layers/compositions.md @@ -36,17 +36,17 @@ compositions/ ## Product ownership -**SLM-CMP-001 - ОБЯЗАН.** Product flow и его локальная product logic должны принадлежать минимальной composition, охватывающей всех consumers этой ответственности. +**SLM-BASE-CMP-001 - ОБЯЗАН.** Product flow и его локальная product logic должны принадлежать минимальной composition, охватывающей всех consumers этой ответственности. Composition может использовать public API `infra` для external operations, сохраняя product mapping, outcomes и fallback semantics у себя. ## Public boundaries -**SLM-CMP-005 - ЗАПРЕЩЕНО.** Composition не может импортировать private services, integrations, stores, Context или другие internal paths используемого module. +**SLM-BASE-CMP-005 - ЗАПРЕЩЕНО.** Composition не может импортировать private services, integrations, stores, Context или другие internal paths используемого module. ## Product UI -**SLM-CMP-006 - ОБЯЗАН.** UI, объединяющий несколько самостоятельных modules, route/page scope либо application flow, принадлежит `compositions`. +**SLM-BASE-CMP-006 - ОБЯЗАН.** UI, объединяющий несколько самостоятельных modules, route/page scope либо application flow, принадлежит `compositions`. Примеры: @@ -56,11 +56,11 @@ Composition может использовать public API `infra` для extern - route guard с navigation outcome; - widget, использующий public APIs двух самостоятельных modules. -**SLM-CMP-007 - МОЖЕТ.** Composition может использовать product UI, опубликованный другими modules, и universal UI, передавая props, callbacks и slots. +**SLM-BASE-CMP-007 - МОЖЕТ.** Composition может использовать product UI, опубликованный другими modules, и universal UI, передавая props, callbacks и slots. ## State -**SLM-CMP-008 - ОБЯЗАН.** Page-local presentation state принадлежит минимальной composition, охватывающей всех его consumers. +**SLM-BASE-CMP-008 - ОБЯЗАН.** Page-local presentation state принадлежит минимальной composition, охватывающей всех его consumers. Примеры page-local state: @@ -70,15 +70,15 @@ Composition может использовать public API `infra` для extern - presentation filters; - состояние раскрытия section. -**SLM-CMP-009 - ЗАПРЕЩЕНО.** Page store не может становиться параллельным владельцем product model или canonical product cache другого owner. +**SLM-BASE-CMP-009 - ЗАПРЕЩЕНО.** Page store не может становиться параллельным владельцем product model или canonical product cache другого owner. ## Imports -**SLM-CMP-010 - МОЖЕТ.** Composition module может импортировать public API других composition modules, infra, ui и shared. +**SLM-BASE-CMP-010 - МОЖЕТ.** Composition module может импортировать public API других composition modules, infra, ui и shared. -Runtime-циклы между composition modules запрещены base-правилом `SLM-API-016`. +Runtime-циклы между composition modules запрещены base-правилом `SLM-BASE-API-016`. -**SLM-CMP-015 - ОБЯЗАН.** Client и server composition entries должны иметь раздельные public entrypoints и environment markers, если composition участвует в обоих runtime graphs. +**SLM-BASE-CMP-015 - ОБЯЗАН.** Client и server composition entries должны иметь раздельные public entrypoints и environment markers, если composition участвует в обоих runtime graphs. ## Scope diff --git a/docs2/specification/layers/index.md b/docs/ru/specification/layers/index.md similarity index 62% rename from docs2/specification/layers/index.md rename to docs/ru/specification/layers/index.md index 23d2fce..c683c14 100644 --- a/docs2/specification/layers/index.md +++ b/docs/ru/specification/layers/index.md @@ -20,15 +20,15 @@ normative: true ## Общие правила -**SLM-LAY-001 - ОБЯЗАН.** Module должен располагаться в слое, который владеет его основной ответственностью. +**SLM-BASE-LAY-001 - ОБЯЗАН.** Module должен располагаться в слое, который владеет его основной ответственностью. -**SLM-LAY-002 - ЗАПРЕЩЕНО.** Нельзя выбирать слой по техническому типу файла без определения владельца поведения и данных. +**SLM-BASE-LAY-002 - ЗАПРЕЩЕНО.** Нельзя выбирать слой по техническому типу файла без определения владельца поведения и данных. -**SLM-LAY-003 - ОБЯЗАН.** Межслойный import должен одновременно соответствовать общей dependency direction и public API импортируемого module. +**SLM-BASE-LAY-003 - ОБЯЗАН.** Межслойный import должен одновременно соответствовать общей dependency direction и public API импортируемого module. -**SLM-LAY-004 - ЗАПРЕЩЕНО.** Нельзя создавать proxy module в разрешённом слое только для обхода запрещённого направления import. +**SLM-BASE-LAY-004 - ЗАПРЕЩЕНО.** Нельзя создавать proxy module в разрешённом слое только для обхода запрещённого направления import. -**SLM-LAY-005 - СЛЕДУЕТ.** При смешанной ответственности module следует разделить по реальным владельцам. Application flow и UI нескольких самостоятельных modules следует собирать в `compositions`. +**SLM-BASE-LAY-005 - СЛЕДУЕТ.** При смешанной ответственности module следует разделить по реальным владельцам. Application flow и UI нескольких самостоятельных modules следует собирать в `compositions`. ## Выбор слоя diff --git a/docs/ru/specification/layers/infra.md b/docs/ru/specification/layers/infra.md new file mode 100644 index 0000000..b86cf58 --- /dev/null +++ b/docs/ru/specification/layers/infra.md @@ -0,0 +1,53 @@ +--- +title: Слой Infra +status: draft +normative: true +--- + +# Слой Infra + +`infra` содержит technical capabilities приложения, не определяющие product model и scenarios. + +## Примеры modules + +```text +infra/ +├── http/ +├── backend-api/ +├── realtime/ +├── analytics/ +├── logger/ +├── app-config/ +├── storage/ +├── i18n/ +└── theme/ +``` + +## Правила + +**SLM-BASE-INF-001 - ОБЯЗАН.** Infra module должен описывать technical capability, а не product semantics или scenario. + +**SLM-BASE-INF-002 - МОЖЕТ.** Infra module может импортировать public API другого infra module и `shared`. + +Запрет infra импортировать `compositions` или `app` определяется base-правилом `SLM-BASE-ARCH-003`. + +**SLM-BASE-INF-004 - ЗАПРЕЩЕНО.** Infra не может владеть product wiring, собирать application graph или предоставлять generic product service locator. + +**SLM-BASE-INF-005 - ЗАПРЕЩЕНО.** Infra не создаёт product errors, product fallback и product model из transport DTO. + +**SLM-BASE-INF-006 - МОЖЕТ.** Infra может экспортировать technical client, transport, event source, storage primitive или platform wrapper через собственный public API. + +**SLM-BASE-INF-007 - ОБЯЗАН.** Generated SDK и transport details должны оставаться внутри technical или private integration boundary владельца и не становиться частью public product contract. + +## Product integration + +Infra знает technical mechanism: + +```text +HTTP client +WebSocket transport +local storage primitive +analytics SDK +``` + +Product owner определяет semantics использования capability; infra предоставляет механизм через public API. Один infra module может использоваться несколькими product owners без знания их semantics. diff --git a/docs/ru/specification/layers/shared.md b/docs/ru/specification/layers/shared.md new file mode 100644 index 0000000..f55e5d9 --- /dev/null +++ b/docs/ru/specification/layers/shared.md @@ -0,0 +1,42 @@ +--- +title: Слой Shared +status: draft +normative: true +--- + +# Слой Shared + +`shared` является детерминированным фундаментом приложения и не знает о SLM-модулях верхних слоёв. + +## Допустимое содержимое + +- pure utilities; +- value predicates; +- product-agnostic types; +- styling foundation и tokens; +- static resources; +- compile-time constants без product ownership; +- deterministic formatting primitives. + +## Правила + +**SLM-BASE-SHR-001 - ОБЯЗАН.** Результат shared utility должен определяться явными аргументами и не зависеть от скрытого runtime environment. + +**SLM-BASE-SHR-002 - ЗАПРЕЩЕНО.** `shared` не может импортировать `app`, `compositions`, `infra` или `ui`. + +**SLM-BASE-SHR-003 - ЗАПРЕЩЕНО.** `shared` не может владеть product types, product rules, runtime state, I/O, storage access или event subscriptions. + +**SLM-BASE-SHR-004 - ЗАПРЕЩЕНО.** Нельзя переносить product helper, DTO, integration contract или product config в `shared` для обхода import boundary. + +**SLM-BASE-SHR-005 - СЛЕДУЕТ.** Код следует поднимать в `shared` только при подтверждённой product-agnostic semantics, а не из-за повторения нескольких строк. + +## Отличие от других слоёв + +| Код | Владелец | +|---|---| +| Email validator с product rules | Владеющий product module | +| Generic string trim utility | `shared` | +| Browser storage wrapper | `infra` | +| Product storage integration | Product owner; storage primitive - `infra` | +| UI spacing tokens | `shared` | +| Button consuming spacing tokens | `ui` | diff --git a/docs/ru/specification/layers/ui.md b/docs/ru/specification/layers/ui.md new file mode 100644 index 0000000..a358907 --- /dev/null +++ b/docs/ru/specification/layers/ui.md @@ -0,0 +1,48 @@ +--- +title: Слой UI +status: draft +normative: true +--- + +# Слой UI + +`ui` содержит reusable presentation modules без product scenario и product ownership. + +## Примеры + +```text +ui/ +├── button/ +├── input/ +├── icon/ +├── modal/ +├── carousel/ +├── tabs/ +└── tooltip/ +``` + +## Правила + +**SLM-BASE-UI-001 - ОБЯЗАН.** UI module должен быть применим без product-specific knowledge. + +**SLM-BASE-UI-002 - ЗАПРЕЩЕНО.** UI module не может импортировать `compositions`, `app` или product-specific infra. + +**SLM-BASE-UI-003 - МОЖЕТ.** UI module может импортировать public API других UI modules и `shared`. + +**SLM-BASE-UI-004 - ЗАПРЕЩЕНО.** UI module не выбирает product data source, не вызывает product scenario и не владеет multi-module behavior. + +**SLM-BASE-UI-005 - МОЖЕТ.** UI module может владеть локальным interaction state, необходимым только для собственной presentation mechanics. + +**SLM-BASE-UI-006 - ОБЯЗАН.** Компонент с product semantics должен принадлежать владеющему product module, а не `ui`. + +## Классификация + +| Сущность | Владелец | +|---|---| +| `Button`, `Input`, `Modal` | `ui` | +| `LoginForm` одной auth responsibility | Владеющий product module | +| Application header | `compositions` | +| Generic date picker | `ui` | +| Medication schedule | Владеющий product module согласно ownership | + +Универсальность определяется отсутствием product knowledge, а не количеством текущих consumers. diff --git a/docs2/specification/modes/advanced/domains.md b/docs/ru/specification/modes/advanced/domains.md similarity index 95% rename from docs2/specification/modes/advanced/domains.md rename to docs/ru/specification/modes/advanced/domains.md index 00632bb..8c826c3 100644 --- a/docs2/specification/modes/advanced/domains.md +++ b/docs/ru/specification/modes/advanced/domains.md @@ -68,7 +68,7 @@ domains/knv/auth/ Это пример, а не обязательный scaffold. Небольшой domain может состоять из одного файла и public entrypoint. -Domain может хранить файлы в корне и использовать любые необходимые segments согласно base-правилам [SLM-SEG-001 - SLM-SEG-003](../../segments.md#правила). +Domain может хранить файлы в корне и использовать любые необходимые segments согласно base-правилам [SLM-BASE-SEG-001 - SLM-BASE-SEG-003](../../segments.md#правила). **SLM-ADV-DOM-006 - ЗАПРЕЩЕНО.** Нельзя создавать пустые segments или копировать полную структуру другого domain без текущей ответственности. @@ -76,7 +76,7 @@ Domain может хранить файлы в корне и использов ## Public API -Public boundary Advanced domain следует base-правилам `SLM-API-001` и `SLM-API-002`. +Public boundary Advanced domain следует base-правилам `SLM-BASE-API-001` и `SLM-BASE-API-002`. **SLM-ADV-DOM-009 - МОЖЕТ.** Public API domain может экспортировать выбранные командой hooks, Providers, Context, components, service APIs, store access APIs и types как стабильный contract. @@ -93,7 +93,7 @@ domain -> domain | infra | ui | shared **SLM-ADV-DOM-012 - МОЖЕТ.** Domain может напрямую использовать public API `infra`, `ui` и `shared` без обязательной промежуточной abstraction. -Runtime cycles запрещены base-правилом `SLM-API-016`. +Runtime cycles запрещены base-правилом `SLM-BASE-API-016`. **SLM-ADV-DOM-013 - ЗАПРЕЩЕНО.** Type-only dependency cycle между domains запрещён, даже если runtime graph остаётся ацикличным. @@ -113,7 +113,7 @@ composition **SLM-ADV-DOM-015 - МОЖЕТ.** Product UI одной domain responsibility может принадлежать этому domain. -UI нескольких самостоятельных responsibilities остаётся в `compositions` согласно base-правилу `SLM-CMP-006`. +UI нескольких самостоятельных responsibilities остаётся в `compositions` согласно base-правилу `SLM-BASE-CMP-006`. ## Monorepo boundary diff --git a/docs2/specification/modes/advanced/index.md b/docs/ru/specification/modes/advanced/index.md similarity index 93% rename from docs2/specification/modes/advanced/index.md rename to docs/ru/specification/modes/advanced/index.md index 9ed08b4..8a7964d 100644 --- a/docs2/specification/modes/advanced/index.md +++ b/docs/ru/specification/modes/advanced/index.md @@ -49,11 +49,11 @@ Base dependency direction для остальных слоёв сохраняе ## Изменение product ownership -**SLM-ADV-CMP-001 - ОБЯЗАН.** Если product responsibility получила domain owner, Advanced заменяет для этой ответственности base-правило `SLM-CMP-001`: domain владеет собственной product logic, а composition владеет application flow и связывает public APIs. +**SLM-ADV-CMP-001 - ОБЯЗАН.** Если product responsibility получила domain owner, Advanced заменяет для этой ответственности base-правило `SLM-BASE-CMP-001`: domain владеет собственной product logic, а composition владеет application flow и связывает public APIs. Product logic без domain owner продолжает следовать base SLM и принадлежит минимальной composition. -**SLM-ADV-CMP-010 - МОЖЕТ.** Composition module может импортировать public API Advanced domains в дополнение к imports, разрешённым base-правилом `SLM-CMP-010`. +**SLM-ADV-CMP-010 - МОЖЕТ.** Composition module может импортировать public API Advanced domains в дополнение к imports, разрешённым base-правилом `SLM-BASE-CMP-010`. ## Advanced Domain Specification diff --git a/docs2/specification/modes/pro/domains/business.md b/docs/ru/specification/modes/pro/domains/business.md similarity index 100% rename from docs2/specification/modes/pro/domains/business.md rename to docs/ru/specification/modes/pro/domains/business.md diff --git a/docs2/specification/modes/pro/domains/client-and-server.md b/docs/ru/specification/modes/pro/domains/client-and-server.md similarity index 96% rename from docs2/specification/modes/pro/domains/client-and-server.md rename to docs/ru/specification/modes/pro/domains/client-and-server.md index c370d35..f3c9b01 100644 --- a/docs2/specification/modes/pro/domains/client-and-server.md +++ b/docs/ru/specification/modes/pro/domains/client-and-server.md @@ -69,7 +69,7 @@ Server technical inputs ограничены request/framework data, server envi Assembly определяет способ создания, но не владеет полным cross-domain graph. -Отсутствие product request, socket connection и background resource при вызове runtime creator определяется base-правилом `SLM-LIFE-002`. +Отсутствие product request, socket connection и background resource при вызове runtime creator определяется base-правилом `SLM-BASE-LIFE-002`. ```text module import @@ -100,6 +100,6 @@ explicit start Client и server runtimes являются разными instances над общей business semantics. -Запрет на передачу DomainRuntime, functions, Context, store или query client через serializable server/client boundary определяется правилом [SLM-DATA-012](../../../state-and-data.md#serializable-boundaries). +Запрет на передачу DomainRuntime, functions, Context, store или query client через serializable server/client boundary определяется правилом [SLM-BASE-DATA-012](../../../state-and-data.md#serializable-boundaries). **SLM-PRO-ASM-014 - МОЖЕТ.** Server может передать client assembly только serializable business-owned bootstrap data без secrets и mutable runtime objects. diff --git a/docs2/specification/modes/pro/domains/cross-domain-boundary.md b/docs/ru/specification/modes/pro/domains/cross-domain-boundary.md similarity index 100% rename from docs2/specification/modes/pro/domains/cross-domain-boundary.md rename to docs/ru/specification/modes/pro/domains/cross-domain-boundary.md diff --git a/docs2/specification/modes/pro/domains/framework.md b/docs/ru/specification/modes/pro/domains/framework.md similarity index 98% rename from docs2/specification/modes/pro/domains/framework.md rename to docs/ru/specification/modes/pro/domains/framework.md index da98de9..6f84a5d 100644 --- a/docs2/specification/modes/pro/domains/framework.md +++ b/docs/ru/specification/modes/pro/domains/framework.md @@ -70,7 +70,7 @@ Domain React UI может: **SLM-PRO-FRM-013 - ЗАПРЕЩЕНО.** Domain UI не может импортировать runtime другого domain или оркестрировать route/page flow. -Владение React UI, использующим несколько domains, определено base-правилом [SLM-CMP-006](../../../layers/compositions.md#product-ui). +Владение React UI, использующим несколько domains, определено base-правилом [SLM-BASE-CMP-006](../../../layers/compositions.md#product-ui). ## Client boundary diff --git a/docs2/specification/modes/pro/domains/index.md b/docs/ru/specification/modes/pro/domains/index.md similarity index 96% rename from docs2/specification/modes/pro/domains/index.md rename to docs/ru/specification/modes/pro/domains/index.md index ecfb958..e3a97c4 100644 --- a/docs2/specification/modes/pro/domains/index.md +++ b/docs/ru/specification/modes/pro/domains/index.md @@ -52,11 +52,11 @@ Pro domain может владеть: **SLM-PRO-DOM-005 - ЗАПРЕЩЕНО.** Domain не может владеть framework route entry, page/layout composition, UI нескольких самостоятельных product responsibilities, universal technical capability или product-agnostic UI primitive. -Public entrypoints Pro domain следуют base-правилам `SLM-API-001` и `SLM-API-002`; Pro-главы вводят дополнительные ограничения exports. +Public entrypoints Pro domain следуют base-правилам `SLM-BASE-API-001` и `SLM-BASE-API-002`; Pro-главы вводят дополнительные ограничения exports. **SLM-PRO-DOM-007 - ЗАПРЕЩЕНО.** Если product responsibility получила Pro domain owner, app, composition или infra не могут создавать параллельную модель этой ответственности либо обходить её public boundary. -**SLM-PRO-DOM-008 - ОБЯЗАН.** Для domain-owned responsibility это правило заменяет base-правило `SLM-CMP-001`: business владеет product logic, а composition владеет application flow и runtime graph. +**SLM-PRO-DOM-008 - ОБЯЗАН.** Для domain-owned responsibility это правило заменяет base-правило `SLM-BASE-CMP-001`: business владеет product logic, а composition владеет application flow и runtime graph. Product responsibility считается устойчивой, если имеет самостоятельную product model или transitions, используется несколькими application flows либо владеет external integration/lifecycle contract. @@ -128,7 +128,7 @@ Stateless logic API также является DomainRuntime, если он с **SLM-PRO-DOM-014 - МОЖЕТ.** Product UI одной Pro domain responsibility может принадлежать framework surface этого domain. -UI нескольких самостоятельных responsibilities остаётся в `compositions` согласно base-правилу `SLM-CMP-006`. +UI нескольких самостоятельных responsibilities остаётся в `compositions` согласно base-правилу `SLM-BASE-CMP-006`. ## Cross-domain graph diff --git a/docs2/specification/modes/pro/domains/ports-and-adapters.md b/docs/ru/specification/modes/pro/domains/ports-and-adapters.md similarity index 100% rename from docs2/specification/modes/pro/domains/ports-and-adapters.md rename to docs/ru/specification/modes/pro/domains/ports-and-adapters.md diff --git a/docs2/specification/modes/pro/domains/testing.md b/docs/ru/specification/modes/pro/domains/testing.md similarity index 100% rename from docs2/specification/modes/pro/domains/testing.md rename to docs/ru/specification/modes/pro/domains/testing.md diff --git a/docs2/specification/modes/pro/index.md b/docs/ru/specification/modes/pro/index.md similarity index 98% rename from docs2/specification/modes/pro/index.md rename to docs/ru/specification/modes/pro/index.md index 70900fc..be7fb46 100644 --- a/docs2/specification/modes/pro/index.md +++ b/docs/ru/specification/modes/pro/index.md @@ -48,7 +48,7 @@ domains -> согласно внутренним Pro zones Base dependency direction для остальных слоёв сохраняется. -**SLM-PRO-CMP-010 - МОЖЕТ.** Composition module может импортировать public entrypoints Pro domains в дополнение к imports, разрешённым base-правилом `SLM-CMP-010`. +**SLM-PRO-CMP-010 - МОЖЕТ.** Composition module может импортировать public entrypoints Pro domains в дополнение к imports, разрешённым base-правилом `SLM-BASE-CMP-010`. ## Pro Domain Specification diff --git a/docs/ru/specification/modules-and-groups.md b/docs/ru/specification/modules-and-groups.md new file mode 100644 index 0000000..753f283 --- /dev/null +++ b/docs/ru/specification/modules-and-groups.md @@ -0,0 +1,72 @@ +--- +title: Модули и группы +status: draft +normative: true +--- + +# Модули и Группы + +## Module + +Module является минимальным самостоятельным владельцем ответственности и предоставляет public boundary внешнему коду. + +**SLM-BASE-MOD-001 - ОБЯЗАН.** Module должен иметь одну сформулированную ответственность и одного архитектурного owner. + +**SLM-BASE-MOD-002 - ОБЯЗАН.** Внешний consumer взаимодействует с module только через его public API. + +**SLM-BASE-MOD-003 - СЛЕДУЕТ.** Module следует ограничивать только теми внутренними parts и segments, которые необходимы текущей ответственности. + +Типичные modules: + +- page, layout, screen или widget в `compositions`; +- technical service в `infra`; +- reusable UI module в `ui`. + +`app` содержит framework entries и не обязан организовываться как SLM modules. `shared` может содержать небольшие public units, но не runtime modules. + +## Group + +Group классифицирует modules и другие groups, но не владеет поведением. + +**SLM-BASE-MOD-004 - ЗАПРЕЩЕНО.** Group не может иметь `index.ts`, public API, state, runtime, dependencies или assembly. + +**SLM-BASE-MOD-005 - ЗАПРЕЩЕНО.** Внешний код не может импортировать group path. + +**SLM-BASE-MOD-006 - МОЖЕТ.** Group может содержать другие groups и конечные modules. + +```text +compositions/ +└── pages/ # group + ├── home/ # composition module + └── profile/ # composition module +``` + +## Component + +Component является presentation unit внутри module и не считается самостоятельным архитектурным owner. + +**SLM-BASE-MOD-008 - ЗАПРЕЩЕНО.** Component не может самостоятельно выбирать application-level product source, выполнять module wiring или оркестрировать несколько самостоятельных modules. + +**SLM-BASE-MOD-009 - МОЖЕТ.** Component может владеть локальной presentation mechanics и рендерить другие components, разрешённые слоем владельца. + +**SLM-BASE-MOD-010 - ОБЯЗАН.** Presentation unit с самостоятельной ответственностью, внешними архитектурными dependencies или внутренней modular structure должна оформляться как module или nested module. Сам факт локального hook/state не делает component модулем. + +## Nested module + +Самостоятельная часть родительского module может быть оформлена nested module, если имеет собственную ответственность и public boundary только внутри родителя. + +```text +compositions/pages/home/ +└── parts/ + └── hero-section/ + ├── hero-section.tsx + └── index.ts +``` + +**SLM-BASE-MOD-011 - ЗАПРЕЩЕНО.** Nested module не может использоваться для сокрытия ответственности, которой фактически владеет другой module или layer. + +## Scope evolution + +**SLM-BASE-MOD-012 - СЛЕДУЕТ.** Код следует поднимать из локального owner в более широкий module только после появления реального совместного consumer или общей ответственности. + +**SLM-BASE-MOD-013 - ЗАПРЕЩЕНО.** Физическое повторение само по себе не доказывает общий ownership. diff --git a/docs/ru/specification/monorepo.md b/docs/ru/specification/monorepo.md new file mode 100644 index 0000000..843373a --- /dev/null +++ b/docs/ru/specification/monorepo.md @@ -0,0 +1,54 @@ +--- +title: Монорепозитории +status: draft +normative: true +--- + +# Монорепозитории + +SLM применяется внутри границы каждого frontend-приложения. Workspace packages имеют собственные public boundaries и ownership. + +## Application boundary + +```text +apps/ +└── web/ + └── src/ + ├── app/ + ├── compositions/ + ├── infra/ + ├── ui/ + └── shared/ +``` + +**SLM-BASE-MONO-001 - ОБЯЗАН.** Каждое приложение должно самостоятельно определять свои application compositions, product ownership и runtime wiring. + +**SLM-BASE-MONO-002 - ЗАПРЕЩЕНО.** Workspace package не может импортировать код из `apps/*`. + +**SLM-BASE-MONO-003 - ЗАПРЕЩЕНО.** Одно приложение не может deep-import исходники другого приложения вместо общего package contract. + +## Package boundary + +**SLM-BASE-MONO-004 - ОБЯЗАН.** Package должен иметь самостоятельного owner, public exports и подтверждённую reuse/ownership semantics. + +**SLM-BASE-MONO-005 - ЗАПРЕЩЕНО.** Нельзя создавать package только для обхода layer direction, public API или иной объявленной dependency boundary. + +**SLM-BASE-MONO-006 - ОБЯЗАН.** Consumers импортируют package через объявленный package export, а не через filesystem path к internal source. + +## Типичные packages + +Допустимыми кандидатами являются: + +- product-agnostic UI kit; +- technical infra client; +- deterministic shared foundation; +- schema/codegen/tooling package; +- configuration package без application-specific wiring. + +Base SLM не присваивает package дополнительный архитектурный статус автоматически. + +## Dependency direction + +**SLM-BASE-MONO-009 - ОБЯЗАН.** Package dependency graph должен оставаться ацикличным и соответствовать заявленной ответственности packages. + +**SLM-BASE-MONO-010 - ЗАПРЕЩЕНО.** Shared package не может импортировать application composition или app-specific infra package. diff --git a/docs/ru/specification/public-api-and-imports.md b/docs/ru/specification/public-api-and-imports.md new file mode 100644 index 0000000..9791fb6 --- /dev/null +++ b/docs/ru/specification/public-api-and-imports.md @@ -0,0 +1,55 @@ +--- +title: Public API и импорты +status: draft +normative: true +--- + +# Public API и Импорты + +Public API ограничивает знание consumers о внутренней структуре module. Точная форма entrypoint определяется владельцем и не требует обязательного `index.ts`. + +## Общие правила + +**SLM-BASE-API-001 - ОБЯЗАН.** Межмодульный import должен использовать объявленный public entrypoint импортируемого module. + +**SLM-BASE-API-002 - ЗАПРЕЩЕНО.** Deep imports во внутренние segments, files и иные private paths другого module запрещены. + +**SLM-BASE-API-003 - ОБЯЗАН.** Каждый runtime export должен иметь реального consumer за пределами владеющего entrypoint и стабильную ответственность. + +**SLM-BASE-API-004 - ЗАПРЕЩЕНО.** Public API не может случайно раскрывать implementation unit, который владелец считает private или lifecycle которого не является частью public contract. + +**SLM-BASE-API-005 - ОБЯЗАН.** Alias или package subpath должен физически разрешаться TypeScript, tests и production build. + +**SLM-BASE-API-009 - МОЖЕТ.** Public entrypoint может быть root `index.ts`, отдельным named entry, package export или другим явно объявленным path. + +**SLM-BASE-API-010 - ОБЯЗАН.** Public и private paths module должны быть различимы consumers и repository tooling. + +## Layer matrix + +| Importer | Runtime imports | +|---|---| +| `app` | Public composition entries, shared static/global resources | +| `compositions` | Compositions, infra, ui, shared | +| `infra` | Infra, shared | +| `ui` | UI, shared | +| `shared` | External pure libraries only | + +## Type-only imports + +**SLM-BASE-API-006 - МОЖЕТ.** `import type` может использоваться для разрешённого contract dependency без создания runtime edge. + +**SLM-BASE-API-007 - ЗАПРЕЩЕНО.** Type-only import не разрешает перенос ownership, импорт private concrete runtime type или обход layer boundary. + +## Groups + +Отсутствие public entrypoint у group определяется base-правилом `SLM-BASE-MOD-004`. + +**SLM-BASE-API-015 - ОБЯЗАН.** Composition public API экспортирует только entry components, access APIs, types и contracts, необходимые внешним composition consumers. + +## Cycles + +**SLM-BASE-API-016 - ЗАПРЕЩЕНО.** Runtime import cycle между modules запрещён независимо от того, способен ли bundler его выполнить. + +**SLM-BASE-API-017 - ЗАПРЕЩЕНО.** Barrel не должен создавать скрытый cycle между ready composition и access API её children. + +Дополнительные entrypoints и import restrictions принадлежат overlay, который их вводит. diff --git a/docs/ru/specification/rules.md b/docs/ru/specification/rules.md new file mode 100644 index 0000000..592250a --- /dev/null +++ b/docs/ru/specification/rules.md @@ -0,0 +1,13 @@ +--- +title: Реестр правил +status: draft +normative: false +search: false +aside: false +--- + +# Реестр Правил + +Реестр формируется автоматически из нормативных объявлений Specification. Для быстрого перехода к известному ID также можно открыть поиск `Ctrl/⌘ K`, ввести полный идентификатор и нажать `Enter`. + + diff --git a/docs/ru/specification/runtime-and-lifecycle.md b/docs/ru/specification/runtime-and-lifecycle.md new file mode 100644 index 0000000..b497702 --- /dev/null +++ b/docs/ru/specification/runtime-and-lifecycle.md @@ -0,0 +1,83 @@ +--- +title: Runtime и lifecycle +status: draft +normative: true +--- + +# Runtime и Lifecycle + +Lifecycle является архитектурной частью любого mutable runtime, subscription и external resource. Эти правила не требуют создавать отдельный runtime или factory, если у module нет соответствующего состояния или resources. + +## Definition, creation и activation + +Для module с создаваемым runtime применима модель: + +```text +definition + -> module объявляет creator + +creation + -> creator создаёт instance без external effects + +activation + -> scope owner запускает resources и получает cleanup +``` + +**SLM-BASE-LIFE-001 - ЗАПРЕЩЕНО.** Module import не должен выполнять product I/O, открывать connection или регистрировать global listener. + +**SLM-BASE-LIFE-002 - ОБЯЗАН.** Если module предоставляет factory или runtime creator, creation должна быть side-effect free относительно external resources. + +**SLM-BASE-LIFE-003 - ОБЯЗАН.** Subscription, socket, timer и listener запускаются явной operation владельца scope. + +**SLM-BASE-LIFE-004 - ОБЯЗАН.** Каждый запущенный resource должен иметь cleanup или dispose contract. + +## Scope + +| Scope | Примеры владельца | +|---|---| +| Application | Root composition/provider | +| Route branch | Route layout composition | +| Page | Page composition/provider | +| Component flow | Nested composition module | +| Request | Server composition/request builder | +| Test | Test setup/wrapper | + +**SLM-BASE-LIFE-005 - ОБЯЗАН.** Scope owner должен определить количество instances и duration каждого mutable runtime или resource. + +**SLM-BASE-LIFE-006 - ЗАПРЕЩЕНО.** Module-level singleton не может использоваться как случайная замена application scope. + +**SLM-BASE-LIFE-007 - МОЖЕТ.** Application singleton допустим только при явном application ownership и отсутствии request-, identity- и user-specific data. + +## Activation и cleanup + +**SLM-BASE-LIFE-009 - ОБЯЗАН.** Повторный mount/unmount, включая development Strict Mode, не должен оставлять duplicate subscription или abandoned resource. + +**SLM-BASE-LIFE-010 - СЛЕДУЕТ.** `start` и cleanup следует проектировать idempotent либо явно защищать от повторного вызова. + +**SLM-BASE-LIFE-018 - ОБЯЗАН.** Если activation составного resource set завершилась ошибкой, scope owner должен освободить уже успешно запущенную часть в обратном порядке. + +**SLM-BASE-LIFE-019 - ОБЯЗАН.** Ошибка cleanup должна быть наблюдаемой и не должна препятствовать попытке освободить остальные resources scope. + +## Events и sockets + +Product event обрабатывается владельцем product semantics; socket остаётся technical transport. + +**SLM-BASE-LIFE-011 - ЗАПРЕЩЕНО.** Framework component не может открывать product socket напрямую при render или module import. + +**SLM-BASE-LIFE-012 - ОБЯЗАН.** Invalid event и connection failure должны преобразовываться в product state/outcome либо technical telemetry согласно их semantics; callback error нельзя терять через unobserved throw. + +## Revalidation events + +Event может содержать product update или только сообщать об устаревании данных. + +**SLM-BASE-LIFE-014 - ОБЯЗАН.** Invalidation intent должен выражаться product language и не требовать import конкретной query library в public product contract. + +## Server runtime + +**SLM-BASE-LIFE-015 - ОБЯЗАН.** User-specific server runtime создаётся в request scope. + +**SLM-BASE-LIFE-016 - ЗАПРЕЩЕНО.** Process singleton не может захватывать request headers, cookies, credentials, AbortSignal или user-specific cache. + +**SLM-BASE-LIFE-017 - ОБЯЗАН.** Request cancellation должна передаваться external operations, если runtime и используемая integration поддерживают cancellation. + +Overlay может вводить дополнительные lifecycle boundaries только внутри собственного delta. diff --git a/docs2/specification/segments.md b/docs/ru/specification/segments.md similarity index 56% rename from docs2/specification/segments.md rename to docs/ru/specification/segments.md index a45904b..f4f23b2 100644 --- a/docs2/specification/segments.md +++ b/docs/ru/specification/segments.md @@ -27,15 +27,15 @@ Segment группирует внутренние файлы module по уст ## Правила -**SLM-SEG-001 - МОЖЕТ.** Module может использовать любые необходимые segments и не обязан создавать остальные. +**SLM-BASE-SEG-001 - МОЖЕТ.** Module может использовать любые необходимые segments и не обязан создавать остальные. -**SLM-SEG-002 - ЗАПРЕЩЕНО.** Нельзя создавать полный симметричный набор segments как scaffold без реального содержимого. +**SLM-BASE-SEG-002 - ЗАПРЕЩЕНО.** Нельзя создавать полный симметричный набор segments как scaffold без реального содержимого. -**SLM-SEG-003 - ОБЯЗАН.** Если файл помещён в segment, роль segment должна соответствовать фактической роли файла, а не только его расширению или имени. Файлы могут оставаться в корне небольшого module. +**SLM-BASE-SEG-003 - ОБЯЗАН.** Если файл помещён в segment, роль segment должна соответствовать фактической роли файла, а не только его расширению или имени. Файлы могут оставаться в корне небольшого module. -**SLM-SEG-004 - ЗАПРЕЩЕНО.** Segment не имеет внешнего public API независимо от module owner. +**SLM-BASE-SEG-004 - ЗАПРЕЩЕНО.** Segment не имеет внешнего public API независимо от module owner. -Запрет deep import в segment другого module определяется base-правилом `SLM-API-002`. +Запрет deep import в segment другого module определяется base-правилом `SLM-BASE-API-002`. ## UI и Parts @@ -43,11 +43,11 @@ Segment группирует внутренние файлы module по уст `parts/` содержит nested modules с собственной внутренней структурой и локальным public boundary. -**SLM-SEG-006 - ОБЯЗАН.** Сущность с самостоятельной ответственностью, внешними архитектурными dependencies или nested modules должна размещаться в `parts`, а не маскироваться как плоский component. Локальные presentation hooks/state сами по себе не требуют `parts`. +**SLM-BASE-SEG-006 - ОБЯЗАН.** Сущность с самостоятельной ответственностью, внешними архитектурными dependencies или nested modules должна размещаться в `parts`, а не маскироваться как плоский component. Локальные presentation hooks/state сами по себе не требуют `parts`. ## Hooks -**SLM-SEG-007 - ОБЯЗАН.** Hook принадлежит тому module, чью ответственность и runtime он выражает. +**SLM-BASE-SEG-007 - ОБЯЗАН.** Hook принадлежит тому module, чью ответственность и runtime он выражает. Примеры: diff --git a/docs/ru/specification/state-and-data.md b/docs/ru/specification/state-and-data.md new file mode 100644 index 0000000..49ee235 --- /dev/null +++ b/docs/ru/specification/state-and-data.md @@ -0,0 +1,70 @@ +--- +title: State и data +status: draft +normative: true +--- + +# State и Data + +SLM рассматривает данные и состояние через одного владельца semantics, даже если runtime использует несколько caches и projections. + +## Ownership matrix + +| Вид | Владелец | +|---|---| +| Product model и transitions | Product owner | +| Product source integration | Product owner; technical mechanism остаётся в infra | +| Framework projection product data | Public surface владельца product data | +| Page-local presentation state | Composition | +| Component-local interaction | Владеющий component/module | +| Technical connection/cache state | Infra или runtime-specific owner | +| Request context | Server/framework scope | +| Universal UI state | Владеющий UI module | + +## Product gateway + +**SLM-BASE-DATA-001 - ОБЯЗАН.** Consumer должен получать product data через public boundary владеющего module. + +**SLM-BASE-DATA-002 - ЗАПРЕЩЕНО.** Composition, UI или app не могут маппить transport DTO в параллельную product model, если модель уже имеет другого owner. + +**SLM-BASE-DATA-003 - ОБЯЗАН.** Product owner владеет normalization, validation и semantics отсутствия данных. + +## Product state + +**SLM-BASE-DATA-004 - ОБЯЗАН.** Product state model и допустимые transitions должны определяться product owner независимо от concrete state manager. + +**SLM-BASE-DATA-005 - ЗАПРЕЩЕНО.** Concrete mutable store implementation не может становиться public product contract без явно объявленного владельцем стабильного store access API. + +**SLM-BASE-DATA-006 - ОБЯЗАН.** Mutable product instance должен быть привязан к явному lifecycle scope. + +## Query cache + +Framework или technical query cache может хранить projection результата product query. + +**SLM-BASE-DATA-007 - ОБЯЗАН.** Query/cache consumer за пределами product owner должен использовать public boundary владельца и не может обходить его прямым вызовом private integration или SDK. + +**SLM-BASE-DATA-008 - ЗАПРЕЩЕНО.** Query cache не может объявлять собственную product model, error taxonomy или fallback policy. + +**SLM-BASE-DATA-009 - ОБЯЗАН.** User/session-scoped cache keys и invalidation должны изолировать данные разных identities и scopes без использования secret как публичного key contract. + +Эта draft-версия не предписывает единственное физическое место QueryClient/SWR cache. Конкретная модель оценивается по правилам public boundary владельца, lifecycle и identity isolation. + +**SLM-BASE-DATA-015 - ОБЯЗАН.** Cache instance должен иметь явного creator и scope owner в composition или runtime setup. + +**SLM-BASE-DATA-016 - ОБЯЗАН.** Shared framework cache должен передаваться consumers через framework-supported runtime boundary, а не через import app-specific mutable singleton. + +**SLM-BASE-DATA-017 - ОБЯЗАН.** Scope owner должен очищать или изолировать private cache при смене identity и завершении соответствующего scope. + +## Presentation state + +**SLM-BASE-DATA-010 - МОЖЕТ.** Composition или component может использовать concrete state manager для локального presentation state. + +**SLM-BASE-DATA-011 - ЗАПРЕЩЕНО.** Presentation store не должен копировать canonical product state как второй source of truth. + +## Serializable boundaries + +**SLM-BASE-DATA-012 - ОБЯЗАН.** Через server/client boundary передаются только serializable product-owned data без functions, stores, clients, Context и resources. + +**SLM-BASE-DATA-013 - ЗАПРЕЩЕНО.** Secrets, access tokens и request credentials не должны включаться в client bootstrap snapshot. + +**SLM-BASE-DATA-014 - ОБЯЗАН.** Server и client initial snapshots должны быть согласованы, если framework выполняет hydration одного UI state. diff --git a/docs2/specification/terminology.md b/docs/ru/specification/terminology.md similarity index 100% rename from docs2/specification/terminology.md rename to docs/ru/specification/terminology.md diff --git a/docs/ru/specification/testing-and-conformance.md b/docs/ru/specification/testing-and-conformance.md new file mode 100644 index 0000000..d29c5b2 --- /dev/null +++ b/docs/ru/specification/testing-and-conformance.md @@ -0,0 +1,58 @@ +--- +title: Тестирование и соответствие +status: draft +normative: true +--- + +# Тестирование и Соответствие + +Тесты проверяют public boundaries и runtime risks каждого owner. Base SLM не требует создавать неиспользуемые архитектурные конструкции ради тестовой формы. + +## Risk-based tests + +**SLM-BASE-TEST-018 - ОБЯЗАН.** Tests изменённого module должны покрывать применимые риски его public behavior, data boundaries и lifecycle. + +Типичные риски: + +- public behavior; +- malformed external data; +- rejected dependencies; +- state transitions; +- lifecycle activation и cleanup; +- request и identity isolation; +- client/server boundary; +- отсутствие import-time I/O. + +**SLM-BASE-TEST-008 - ОБЯЗАН.** Client/server import boundary должна проверяться инструментом, понимающим реальный framework module graph, если application имеет раздельные environment entries. DOM unit test не заменяет production build probe. + +Mode-specific test suites принадлежат overlay, который вводит соответствующие конструкции. + +## Architecture conformance + +Типичные mechanically enforceable checks: + +- направление imports; +- deep imports; +- public entrypoints; +- runtime cycles; +- заявленный overlay и его rule set; +- unique rule IDs документации; +- generated artifacts, если они используются. + +**SLM-BASE-TEST-011 - ЗАПРЕЩЕНО.** Документированное правило не считается mechanically enforced, если repository tooling его фактически не проверяет. + +## Единица соответствия + +**SLM-BASE-TEST-014 - ОБЯЗАН.** Application соответствует base SLM, если выполняет все base-правила. Соответствие заявленному overlay оценивается как base-правила с учётом точного scope каждой замены плюс полный rule set выбранного overlay. + +**SLM-BASE-TEST-015 - ОБЯЗАН.** Изменение соответствует заявленной архитектуре, если новые и изменённые modules не создают новых нарушений применимых base-правил или правил выбранного overlay и проходят существующие checks. + +**SLM-BASE-TEST-016 - ОБЯЗАН.** Отступление от правила `СЛЕДУЕТ` должно быть зафиксировано в архитектурном review или принятом decision с указанием причины и scope. + +**SLM-BASE-TEST-017 - ОБЯЗАН.** Manual conformance и mechanical enforcement должны различаться явно; отсутствие автоматической проверки не отменяет применимое нормативное правило. + +## Completion gate + +**SLM-BASE-TEST-012 - ОБЯЗАН.** Изменение считается завершённым только после выполнения ближайших tests, typecheck, lint, build и architecture checks, существующих в repository. + +**SLM-BASE-TEST-013 - ОБЯЗАН.** Невыполненная проверка и остаточный риск должны быть явно указаны в результате работы. diff --git a/docs2/README.md b/docs2/README.md deleted file mode 100644 index 9d66470..0000000 --- a/docs2/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# SLM Design 2.0 Draft - -`docs2/` содержит черновик новой спецификации SLM Design. - -Текущая документация в `docs/` остаётся действующим источником истины до отдельного решения о принятии новой спецификации. Skill и его generated reference пока не используют `docs2/`. - -## Точка входа - -[SLM Design Specification](./specification/index.md) - -Specification определяет base SLM и два независимых [architecture modes](./specification/architecture-modes.md): `SLM Advanced` и `SLM Pro`. Каждый mode является отдельным overlay непосредственно над base SLM. - -## Границы текущего этапа - -На этом этапе в `docs2/` размещается только нормативная спецификация. Учебные материалы, руководства, примеры, справочники и agent skill будут проектироваться после стабилизации правил. diff --git a/docs2/specification/foundations.md b/docs2/specification/foundations.md deleted file mode 100644 index 44c2684..0000000 --- a/docs2/specification/foundations.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Основные инварианты -status: draft -normative: true ---- - -# Основные Инварианты - -SLM Design организует frontend-приложение по владельцам ответственности. Архитектурная единица определяется не типом файла, а тем, кто владеет моделью, поведением, данными, runtime и lifecycle. - -## Ответственность до размещения - -**SLM-FND-001 - ОБЯЗАН.** Перед размещением кода необходимо определить его владельца, public boundary, runtime dependencies и lifecycle scope. - -**SLM-FND-002 - СЛЕДУЕТ.** Код следует размещать в минимальном scope, который полностью владеет его ответственностью. - -**SLM-FND-003 - ЗАПРЕЩЕНО.** Нельзя переносить код в общий слой или общий package только на основании предполагаемого будущего переиспользования. - -## Путь продуктовых данных - -Product data проходят через public boundary текущего владельца согласно [SLM-DATA-001](./state-and-data.md#product-gateway). Внешний сервис может оставаться физическим источником данных, но transport contract не становится product model автоматически. - -## Явные зависимости - -**SLM-FND-007 - ОБЯЗАН.** Runtime capabilities должны поступать владельцу поведения через разрешённые imports, явные arguments или contracts, а не через скрытый service locator или global mutable state. - -**SLM-FND-008 - ЗАПРЕЩЕНО.** Type cast, barrel, alias, dynamic import или helper в `shared` не могут использоваться для обхода применимой архитектурной границы. - -## Public API - -Межмодульное взаимодействие и deep imports регулируются [SLM-API-001 - SLM-API-005](./public-api-and-imports.md#общие-правила). - -## Scope и lifecycle - -Создание, scope, activation и cleanup применимых runtimes и resources определены в [Runtime и lifecycle](./runtime-and-lifecycle.md). - -## Overlays - -Base SLM не вводит дополнительные архитектурные слои и специализированные runtime contracts. Каждый overlay самостоятельно определяет свои добавления и замены base-правил. diff --git a/docs2/specification/layers/infra.md b/docs2/specification/layers/infra.md deleted file mode 100644 index 9cbea19..0000000 --- a/docs2/specification/layers/infra.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Слой Infra -status: draft -normative: true ---- - -# Слой Infra - -`infra` содержит technical capabilities приложения, не определяющие product model и scenarios. - -## Примеры modules - -```text -infra/ -├── http/ -├── backend-api/ -├── realtime/ -├── analytics/ -├── logger/ -├── app-config/ -├── storage/ -├── i18n/ -└── theme/ -``` - -## Правила - -**SLM-INF-001 - ОБЯЗАН.** Infra module должен описывать technical capability, а не product semantics или scenario. - -**SLM-INF-002 - МОЖЕТ.** Infra module может импортировать public API другого infra module и `shared`. - -Запрет infra импортировать `compositions` или `app` определяется base-правилом `SLM-ARCH-003`. - -**SLM-INF-004 - ЗАПРЕЩЕНО.** Infra не может владеть product wiring, собирать application graph или предоставлять generic product service locator. - -**SLM-INF-005 - ЗАПРЕЩЕНО.** Infra не создаёт product errors, product fallback и product model из transport DTO. - -**SLM-INF-006 - МОЖЕТ.** Infra может экспортировать technical client, transport, event source, storage primitive или platform wrapper через собственный public API. - -**SLM-INF-007 - ОБЯЗАН.** Generated SDK и transport details должны оставаться внутри technical или private integration boundary владельца и не становиться частью public product contract. - -## Product integration - -Infra знает technical mechanism: - -```text -HTTP client -WebSocket transport -local storage primitive -analytics SDK -``` - -Product owner определяет semantics использования capability; infra предоставляет механизм через public API. Один infra module может использоваться несколькими product owners без знания их semantics. diff --git a/docs2/specification/layers/shared.md b/docs2/specification/layers/shared.md deleted file mode 100644 index 3805f29..0000000 --- a/docs2/specification/layers/shared.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Слой Shared -status: draft -normative: true ---- - -# Слой Shared - -`shared` является детерминированным фундаментом приложения и не знает о SLM-модулях верхних слоёв. - -## Допустимое содержимое - -- pure utilities; -- value predicates; -- product-agnostic types; -- styling foundation и tokens; -- static resources; -- compile-time constants без product ownership; -- deterministic formatting primitives. - -## Правила - -**SLM-SHR-001 - ОБЯЗАН.** Результат shared utility должен определяться явными аргументами и не зависеть от скрытого runtime environment. - -**SLM-SHR-002 - ЗАПРЕЩЕНО.** `shared` не может импортировать `app`, `compositions`, `infra` или `ui`. - -**SLM-SHR-003 - ЗАПРЕЩЕНО.** `shared` не может владеть product types, product rules, runtime state, I/O, storage access или event subscriptions. - -**SLM-SHR-004 - ЗАПРЕЩЕНО.** Нельзя переносить product helper, DTO, integration contract или product config в `shared` для обхода import boundary. - -**SLM-SHR-005 - СЛЕДУЕТ.** Код следует поднимать в `shared` только при подтверждённой product-agnostic semantics, а не из-за повторения нескольких строк. - -## Отличие от других слоёв - -| Код | Владелец | -|---|---| -| Email validator с product rules | Владеющий product module | -| Generic string trim utility | `shared` | -| Browser storage wrapper | `infra` | -| Product storage integration | Product owner; storage primitive - `infra` | -| UI spacing tokens | `shared` | -| Button consuming spacing tokens | `ui` | diff --git a/docs2/specification/layers/ui.md b/docs2/specification/layers/ui.md deleted file mode 100644 index 8504aa9..0000000 --- a/docs2/specification/layers/ui.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Слой UI -status: draft -normative: true ---- - -# Слой UI - -`ui` содержит reusable presentation modules без product scenario и product ownership. - -## Примеры - -```text -ui/ -├── button/ -├── input/ -├── icon/ -├── modal/ -├── carousel/ -├── tabs/ -└── tooltip/ -``` - -## Правила - -**SLM-UI-001 - ОБЯЗАН.** UI module должен быть применим без product-specific knowledge. - -**SLM-UI-002 - ЗАПРЕЩЕНО.** UI module не может импортировать `compositions`, `app` или product-specific infra. - -**SLM-UI-003 - МОЖЕТ.** UI module может импортировать public API других UI modules и `shared`. - -**SLM-UI-004 - ЗАПРЕЩЕНО.** UI module не выбирает product data source, не вызывает product scenario и не владеет multi-module behavior. - -**SLM-UI-005 - МОЖЕТ.** UI module может владеть локальным interaction state, необходимым только для собственной presentation mechanics. - -**SLM-UI-006 - ОБЯЗАН.** Компонент с product semantics должен принадлежать владеющему product module, а не `ui`. - -## Классификация - -| Сущность | Владелец | -|---|---| -| `Button`, `Input`, `Modal` | `ui` | -| `LoginForm` одной auth responsibility | Владеющий product module | -| Application header | `compositions` | -| Generic date picker | `ui` | -| Medication schedule | Владеющий product module согласно ownership | - -Универсальность определяется отсутствием product knowledge, а не количеством текущих consumers. diff --git a/docs2/specification/modules-and-groups.md b/docs2/specification/modules-and-groups.md deleted file mode 100644 index 0971f64..0000000 --- a/docs2/specification/modules-and-groups.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Модули и группы -status: draft -normative: true ---- - -# Модули и Группы - -## Module - -Module является минимальным самостоятельным владельцем ответственности и предоставляет public boundary внешнему коду. - -**SLM-MOD-001 - ОБЯЗАН.** Module должен иметь одну сформулированную ответственность и одного архитектурного owner. - -**SLM-MOD-002 - ОБЯЗАН.** Внешний consumer взаимодействует с module только через его public API. - -**SLM-MOD-003 - СЛЕДУЕТ.** Module следует ограничивать только теми внутренними parts и segments, которые необходимы текущей ответственности. - -Типичные modules: - -- page, layout, screen или widget в `compositions`; -- technical service в `infra`; -- reusable UI module в `ui`. - -`app` содержит framework entries и не обязан организовываться как SLM modules. `shared` может содержать небольшие public units, но не runtime modules. - -## Group - -Group классифицирует modules и другие groups, но не владеет поведением. - -**SLM-MOD-004 - ЗАПРЕЩЕНО.** Group не может иметь `index.ts`, public API, state, runtime, dependencies или assembly. - -**SLM-MOD-005 - ЗАПРЕЩЕНО.** Внешний код не может импортировать group path. - -**SLM-MOD-006 - МОЖЕТ.** Group может содержать другие groups и конечные modules. - -```text -compositions/ -└── pages/ # group - ├── home/ # composition module - └── profile/ # composition module -``` - -## Component - -Component является presentation unit внутри module и не считается самостоятельным архитектурным owner. - -**SLM-MOD-008 - ЗАПРЕЩЕНО.** Component не может самостоятельно выбирать application-level product source, выполнять module wiring или оркестрировать несколько самостоятельных modules. - -**SLM-MOD-009 - МОЖЕТ.** Component может владеть локальной presentation mechanics и рендерить другие components, разрешённые слоем владельца. - -**SLM-MOD-010 - ОБЯЗАН.** Presentation unit с самостоятельной ответственностью, внешними архитектурными dependencies или внутренней modular structure должна оформляться как module или nested module. Сам факт локального hook/state не делает component модулем. - -## Nested module - -Самостоятельная часть родительского module может быть оформлена nested module, если имеет собственную ответственность и public boundary только внутри родителя. - -```text -compositions/pages/home/ -└── parts/ - └── hero-section/ - ├── hero-section.tsx - └── index.ts -``` - -**SLM-MOD-011 - ЗАПРЕЩЕНО.** Nested module не может использоваться для сокрытия ответственности, которой фактически владеет другой module или layer. - -## Scope evolution - -**SLM-MOD-012 - СЛЕДУЕТ.** Код следует поднимать из локального owner в более широкий module только после появления реального совместного consumer или общей ответственности. - -**SLM-MOD-013 - ЗАПРЕЩЕНО.** Физическое повторение само по себе не доказывает общий ownership. diff --git a/docs2/specification/monorepo.md b/docs2/specification/monorepo.md deleted file mode 100644 index af1811c..0000000 --- a/docs2/specification/monorepo.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Монорепозитории -status: draft -normative: true ---- - -# Монорепозитории - -SLM применяется внутри границы каждого frontend-приложения. Workspace packages имеют собственные public boundaries и ownership. - -## Application boundary - -```text -apps/ -└── web/ - └── src/ - ├── app/ - ├── compositions/ - ├── infra/ - ├── ui/ - └── shared/ -``` - -**SLM-MONO-001 - ОБЯЗАН.** Каждое приложение должно самостоятельно определять свои application compositions, product ownership и runtime wiring. - -**SLM-MONO-002 - ЗАПРЕЩЕНО.** Workspace package не может импортировать код из `apps/*`. - -**SLM-MONO-003 - ЗАПРЕЩЕНО.** Одно приложение не может deep-import исходники другого приложения вместо общего package contract. - -## Package boundary - -**SLM-MONO-004 - ОБЯЗАН.** Package должен иметь самостоятельного owner, public exports и подтверждённую reuse/ownership semantics. - -**SLM-MONO-005 - ЗАПРЕЩЕНО.** Нельзя создавать package только для обхода layer direction, public API или иной объявленной dependency boundary. - -**SLM-MONO-006 - ОБЯЗАН.** Consumers импортируют package через объявленный package export, а не через filesystem path к internal source. - -## Типичные packages - -Допустимыми кандидатами являются: - -- product-agnostic UI kit; -- technical infra client; -- deterministic shared foundation; -- schema/codegen/tooling package; -- configuration package без application-specific wiring. - -Base SLM не присваивает package дополнительный архитектурный статус автоматически. - -## Dependency direction - -**SLM-MONO-009 - ОБЯЗАН.** Package dependency graph должен оставаться ацикличным и соответствовать заявленной ответственности packages. - -**SLM-MONO-010 - ЗАПРЕЩЕНО.** Shared package не может импортировать application composition или app-specific infra package. diff --git a/docs2/specification/public-api-and-imports.md b/docs2/specification/public-api-and-imports.md deleted file mode 100644 index e68249b..0000000 --- a/docs2/specification/public-api-and-imports.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Public API и импорты -status: draft -normative: true ---- - -# Public API и Импорты - -Public API ограничивает знание consumers о внутренней структуре module. Точная форма entrypoint определяется владельцем и не требует обязательного `index.ts`. - -## Общие правила - -**SLM-API-001 - ОБЯЗАН.** Межмодульный import должен использовать объявленный public entrypoint импортируемого module. - -**SLM-API-002 - ЗАПРЕЩЕНО.** Deep imports во внутренние segments, files и иные private paths другого module запрещены. - -**SLM-API-003 - ОБЯЗАН.** Каждый runtime export должен иметь реального consumer за пределами владеющего entrypoint и стабильную ответственность. - -**SLM-API-004 - ЗАПРЕЩЕНО.** Public API не может случайно раскрывать implementation unit, который владелец считает private или lifecycle которого не является частью public contract. - -**SLM-API-005 - ОБЯЗАН.** Alias или package subpath должен физически разрешаться TypeScript, tests и production build. - -**SLM-API-009 - МОЖЕТ.** Public entrypoint может быть root `index.ts`, отдельным named entry, package export или другим явно объявленным path. - -**SLM-API-010 - ОБЯЗАН.** Public и private paths module должны быть различимы consumers и repository tooling. - -## Layer matrix - -| Importer | Runtime imports | -|---|---| -| `app` | Public composition entries, shared static/global resources | -| `compositions` | Compositions, infra, ui, shared | -| `infra` | Infra, shared | -| `ui` | UI, shared | -| `shared` | External pure libraries only | - -## Type-only imports - -**SLM-API-006 - МОЖЕТ.** `import type` может использоваться для разрешённого contract dependency без создания runtime edge. - -**SLM-API-007 - ЗАПРЕЩЕНО.** Type-only import не разрешает перенос ownership, импорт private concrete runtime type или обход layer boundary. - -## Groups - -Отсутствие public entrypoint у group определяется base-правилом `SLM-MOD-004`. - -**SLM-API-015 - ОБЯЗАН.** Composition public API экспортирует только entry components, access APIs, types и contracts, необходимые внешним composition consumers. - -## Cycles - -**SLM-API-016 - ЗАПРЕЩЕНО.** Runtime import cycle между modules запрещён независимо от того, способен ли bundler его выполнить. - -**SLM-API-017 - ЗАПРЕЩЕНО.** Barrel не должен создавать скрытый cycle между ready composition и access API её children. - -Дополнительные entrypoints и import restrictions принадлежат overlay, который их вводит. diff --git a/docs2/specification/runtime-and-lifecycle.md b/docs2/specification/runtime-and-lifecycle.md deleted file mode 100644 index 2cf3624..0000000 --- a/docs2/specification/runtime-and-lifecycle.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Runtime и lifecycle -status: draft -normative: true ---- - -# Runtime и Lifecycle - -Lifecycle является архитектурной частью любого mutable runtime, subscription и external resource. Эти правила не требуют создавать отдельный runtime или factory, если у module нет соответствующего состояния или resources. - -## Definition, creation и activation - -Для module с создаваемым runtime применима модель: - -```text -definition - -> module объявляет creator - -creation - -> creator создаёт instance без external effects - -activation - -> scope owner запускает resources и получает cleanup -``` - -**SLM-LIFE-001 - ЗАПРЕЩЕНО.** Module import не должен выполнять product I/O, открывать connection или регистрировать global listener. - -**SLM-LIFE-002 - ОБЯЗАН.** Если module предоставляет factory или runtime creator, creation должна быть side-effect free относительно external resources. - -**SLM-LIFE-003 - ОБЯЗАН.** Subscription, socket, timer и listener запускаются явной operation владельца scope. - -**SLM-LIFE-004 - ОБЯЗАН.** Каждый запущенный resource должен иметь cleanup или dispose contract. - -## Scope - -| Scope | Примеры владельца | -|---|---| -| Application | Root composition/provider | -| Route branch | Route layout composition | -| Page | Page composition/provider | -| Component flow | Nested composition module | -| Request | Server composition/request builder | -| Test | Test setup/wrapper | - -**SLM-LIFE-005 - ОБЯЗАН.** Scope owner должен определить количество instances и duration каждого mutable runtime или resource. - -**SLM-LIFE-006 - ЗАПРЕЩЕНО.** Module-level singleton не может использоваться как случайная замена application scope. - -**SLM-LIFE-007 - МОЖЕТ.** Application singleton допустим только при явном application ownership и отсутствии request-, identity- и user-specific data. - -## Activation и cleanup - -**SLM-LIFE-009 - ОБЯЗАН.** Повторный mount/unmount, включая development Strict Mode, не должен оставлять duplicate subscription или abandoned resource. - -**SLM-LIFE-010 - СЛЕДУЕТ.** `start` и cleanup следует проектировать idempotent либо явно защищать от повторного вызова. - -**SLM-LIFE-018 - ОБЯЗАН.** Если activation составного resource set завершилась ошибкой, scope owner должен освободить уже успешно запущенную часть в обратном порядке. - -**SLM-LIFE-019 - ОБЯЗАН.** Ошибка cleanup должна быть наблюдаемой и не должна препятствовать попытке освободить остальные resources scope. - -## Events и sockets - -Product event обрабатывается владельцем product semantics; socket остаётся technical transport. - -**SLM-LIFE-011 - ЗАПРЕЩЕНО.** Framework component не может открывать product socket напрямую при render или module import. - -**SLM-LIFE-012 - ОБЯЗАН.** Invalid event и connection failure должны преобразовываться в product state/outcome либо technical telemetry согласно их semantics; callback error нельзя терять через unobserved throw. - -## Revalidation events - -Event может содержать product update или только сообщать об устаревании данных. - -**SLM-LIFE-014 - ОБЯЗАН.** Invalidation intent должен выражаться product language и не требовать import конкретной query library в public product contract. - -## Server runtime - -**SLM-LIFE-015 - ОБЯЗАН.** User-specific server runtime создаётся в request scope. - -**SLM-LIFE-016 - ЗАПРЕЩЕНО.** Process singleton не может захватывать request headers, cookies, credentials, AbortSignal или user-specific cache. - -**SLM-LIFE-017 - ОБЯЗАН.** Request cancellation должна передаваться external operations, если runtime и используемая integration поддерживают cancellation. - -Overlay может вводить дополнительные lifecycle boundaries только внутри собственного delta. diff --git a/docs2/specification/state-and-data.md b/docs2/specification/state-and-data.md deleted file mode 100644 index f9f4565..0000000 --- a/docs2/specification/state-and-data.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: State и data -status: draft -normative: true ---- - -# State и Data - -SLM рассматривает данные и состояние через одного владельца semantics, даже если runtime использует несколько caches и projections. - -## Ownership matrix - -| Вид | Владелец | -|---|---| -| Product model и transitions | Product owner | -| Product source integration | Product owner; technical mechanism остаётся в infra | -| Framework projection product data | Public surface владельца product data | -| Page-local presentation state | Composition | -| Component-local interaction | Владеющий component/module | -| Technical connection/cache state | Infra или runtime-specific owner | -| Request context | Server/framework scope | -| Universal UI state | Владеющий UI module | - -## Product gateway - -**SLM-DATA-001 - ОБЯЗАН.** Consumer должен получать product data через public boundary владеющего module. - -**SLM-DATA-002 - ЗАПРЕЩЕНО.** Composition, UI или app не могут маппить transport DTO в параллельную product model, если модель уже имеет другого owner. - -**SLM-DATA-003 - ОБЯЗАН.** Product owner владеет normalization, validation и semantics отсутствия данных. - -## Product state - -**SLM-DATA-004 - ОБЯЗАН.** Product state model и допустимые transitions должны определяться product owner независимо от concrete state manager. - -**SLM-DATA-005 - ЗАПРЕЩЕНО.** Concrete mutable store implementation не может становиться public product contract без явно объявленного владельцем стабильного store access API. - -**SLM-DATA-006 - ОБЯЗАН.** Mutable product instance должен быть привязан к явному lifecycle scope. - -## Query cache - -Framework или technical query cache может хранить projection результата product query. - -**SLM-DATA-007 - ОБЯЗАН.** Query/cache consumer за пределами product owner должен использовать public boundary владельца и не может обходить его прямым вызовом private integration или SDK. - -**SLM-DATA-008 - ЗАПРЕЩЕНО.** Query cache не может объявлять собственную product model, error taxonomy или fallback policy. - -**SLM-DATA-009 - ОБЯЗАН.** User/session-scoped cache keys и invalidation должны изолировать данные разных identities и scopes без использования secret как публичного key contract. - -Эта draft-версия не предписывает единственное физическое место QueryClient/SWR cache. Конкретная модель оценивается по правилам public boundary владельца, lifecycle и identity isolation. - -**SLM-DATA-015 - ОБЯЗАН.** Cache instance должен иметь явного creator и scope owner в composition или runtime setup. - -**SLM-DATA-016 - ОБЯЗАН.** Shared framework cache должен передаваться consumers через framework-supported runtime boundary, а не через import app-specific mutable singleton. - -**SLM-DATA-017 - ОБЯЗАН.** Scope owner должен очищать или изолировать private cache при смене identity и завершении соответствующего scope. - -## Presentation state - -**SLM-DATA-010 - МОЖЕТ.** Composition или component может использовать concrete state manager для локального presentation state. - -**SLM-DATA-011 - ЗАПРЕЩЕНО.** Presentation store не должен копировать canonical product state как второй source of truth. - -## Serializable boundaries - -**SLM-DATA-012 - ОБЯЗАН.** Через server/client boundary передаются только serializable product-owned data без functions, stores, clients, Context и resources. - -**SLM-DATA-013 - ЗАПРЕЩЕНО.** Secrets, access tokens и request credentials не должны включаться в client bootstrap snapshot. - -**SLM-DATA-014 - ОБЯЗАН.** Server и client initial snapshots должны быть согласованы, если framework выполняет hydration одного UI state. diff --git a/docs2/specification/testing-and-conformance.md b/docs2/specification/testing-and-conformance.md deleted file mode 100644 index d3208a3..0000000 --- a/docs2/specification/testing-and-conformance.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Тестирование и соответствие -status: draft -normative: true ---- - -# Тестирование и Соответствие - -Тесты проверяют public boundaries и runtime risks каждого owner. Base SLM не требует создавать неиспользуемые архитектурные конструкции ради тестовой формы. - -## Risk-based tests - -**SLM-TEST-018 - ОБЯЗАН.** Tests изменённого module должны покрывать применимые риски его public behavior, data boundaries и lifecycle. - -Типичные риски: - -- public behavior; -- malformed external data; -- rejected dependencies; -- state transitions; -- lifecycle activation и cleanup; -- request и identity isolation; -- client/server boundary; -- отсутствие import-time I/O. - -**SLM-TEST-008 - ОБЯЗАН.** Client/server import boundary должна проверяться инструментом, понимающим реальный framework module graph, если application имеет раздельные environment entries. DOM unit test не заменяет production build probe. - -Mode-specific test suites принадлежат overlay, который вводит соответствующие конструкции. - -## Architecture conformance - -Типичные mechanically enforceable checks: - -- направление imports; -- deep imports; -- public entrypoints; -- runtime cycles; -- заявленный overlay и его rule set; -- unique rule IDs документации; -- generated artifacts, если они используются. - -**SLM-TEST-011 - ЗАПРЕЩЕНО.** Документированное правило не считается mechanically enforced, если repository tooling его фактически не проверяет. - -## Единица соответствия - -**SLM-TEST-014 - ОБЯЗАН.** Application соответствует base SLM, если выполняет все base-правила. Соответствие заявленному overlay оценивается как base-правила с учётом точного scope каждой замены плюс полный rule set выбранного overlay. - -**SLM-TEST-015 - ОБЯЗАН.** Изменение соответствует заявленной архитектуре, если новые и изменённые modules не создают новых нарушений применимых base-правил или правил выбранного overlay и проходят существующие checks. - -**SLM-TEST-016 - ОБЯЗАН.** Отступление от правила `СЛЕДУЕТ` должно быть зафиксировано в архитектурном review или принятом decision с указанием причины и scope. - -**SLM-TEST-017 - ОБЯЗАН.** Manual conformance и mechanical enforcement должны различаться явно; отсутствие автоматической проверки не отменяет применимое нормативное правило. - -## Completion gate - -**SLM-TEST-012 - ОБЯЗАН.** Изменение считается завершённым только после выполнения ближайших tests, typecheck, lint, build и architecture checks, существующих в repository. - -**SLM-TEST-013 - ОБЯЗАН.** Невыполненная проверка и остаточный риск должны быть явно указаны в результате работы. diff --git a/docs/README.md b/old-docs/README.md similarity index 79% rename from docs/README.md rename to old-docs/README.md index d6f92b8..0128dae 100644 --- a/docs/README.md +++ b/old-docs/README.md @@ -1,6 +1,6 @@ # SLM Design -`docs/` - файлы документации по SLM-архитектуре. +Этот каталог содержит действующую legacy-документацию по SLM-архитектуре. Используй эту документацию как источник истины для задач по SLM Design: слоям, модулям, сегментам, публичному API, business factory, composition modules и монорепозиториям. diff --git a/docs/canons/business-factory.md b/old-docs/canons/business-factory.md similarity index 100% rename from docs/canons/business-factory.md rename to old-docs/canons/business-factory.md diff --git a/docs/canons/business-runtime-boundary.md b/old-docs/canons/business-runtime-boundary.md similarity index 100% rename from docs/canons/business-runtime-boundary.md rename to old-docs/canons/business-runtime-boundary.md diff --git a/docs/canons/decision-process.md b/old-docs/canons/decision-process.md similarity index 100% rename from docs/canons/decision-process.md rename to old-docs/canons/decision-process.md diff --git a/docs/canons/file-atlas.md b/old-docs/canons/file-atlas.md similarity index 100% rename from docs/canons/file-atlas.md rename to old-docs/canons/file-atlas.md diff --git a/docs/canons/index.md b/old-docs/canons/index.md similarity index 100% rename from docs/canons/index.md rename to old-docs/canons/index.md diff --git a/docs/canons/layers.md b/old-docs/canons/layers.md similarity index 100% rename from docs/canons/layers.md rename to old-docs/canons/layers.md diff --git a/docs/canons/modules.md b/old-docs/canons/modules.md similarity index 100% rename from docs/canons/modules.md rename to old-docs/canons/modules.md diff --git a/docs/canons/monorepo.md b/old-docs/canons/monorepo.md similarity index 100% rename from docs/canons/monorepo.md rename to old-docs/canons/monorepo.md diff --git a/docs/canons/segments.md b/old-docs/canons/segments.md similarity index 100% rename from docs/canons/segments.md rename to old-docs/canons/segments.md diff --git a/docs/canons/validation.md b/old-docs/canons/validation.md similarity index 100% rename from docs/canons/validation.md rename to old-docs/canons/validation.md diff --git a/docs/examples/business-composition.md b/old-docs/examples/business-composition.md similarity index 100% rename from docs/examples/business-composition.md rename to old-docs/examples/business-composition.md diff --git a/docs/examples/business-testing.md b/old-docs/examples/business-testing.md similarity index 100% rename from docs/examples/business-testing.md rename to old-docs/examples/business-testing.md diff --git a/docs/examples/react/composition-provider.md b/old-docs/examples/react/composition-provider.md similarity index 100% rename from docs/examples/react/composition-provider.md rename to old-docs/examples/react/composition-provider.md diff --git a/docs/examples/react/composition-structures.md b/old-docs/examples/react/composition-structures.md similarity index 100% rename from docs/examples/react/composition-structures.md rename to old-docs/examples/react/composition-structures.md diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..534c88e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2521 @@ +{ + "name": "slm-design", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "slm-design", + "devDependencies": { + "minisearch": "7.2.0", + "vitepress": "1.6.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@algolia/abtesting": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.22.0.tgz", + "integrity": "sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.56.0.tgz", + "integrity": "sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.56.0.tgz", + "integrity": "sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.56.0.tgz", + "integrity": "sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.56.0.tgz", + "integrity": "sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.56.0.tgz", + "integrity": "sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.56.0.tgz", + "integrity": "sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.56.0.tgz", + "integrity": "sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.56.0.tgz", + "integrity": "sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.56.0.tgz", + "integrity": "sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.56.0.tgz", + "integrity": "sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.56.0.tgz", + "integrity": "sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.56.0.tgz", + "integrity": "sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.56.0.tgz", + "integrity": "sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.91", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.91.tgz", + "integrity": "sha512-plQJHZxzpkIXaeYjJF0Sc9nkzYF32EDX9olx7j1hcKYFaiTrQXbcngZmvzuV0pG6uZPU3ZuA/GIsgxSHOm6jrQ==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz", + "integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.40", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz", + "integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz", + "integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.40", + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-ssr": "3.5.40", + "@vue/shared": "3.5.40", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz", + "integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.10" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.10", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz", + "integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz", + "integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz", + "integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/runtime-core": "3.5.40", + "@vue/shared": "3.5.40", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz", + "integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz", + "integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/algoliasearch": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.56.0.tgz", + "integrity": "sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.22.0", + "@algolia/client-abtesting": "5.56.0", + "@algolia/client-analytics": "5.56.0", + "@algolia/client-common": "5.56.0", + "@algolia/client-insights": "5.56.0", + "@algolia/client-personalization": "5.56.0", + "@algolia/client-query-suggestions": "5.56.0", + "@algolia/client-search": "5.56.0", + "@algolia/ingestion": "1.56.0", + "@algolia/monitoring": "1.56.0", + "@algolia/recommend": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.29.7", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.7.tgz", + "integrity": "sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tabbable": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz", + "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz", + "integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-sfc": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/server-renderer": "3.5.40", + "@vue/shared": "3.5.40" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json index fb077da..d22434a 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,20 @@ "scripts": { "build": "npm run build:skill", "build:skill": "node scripts/build-skill.mjs", - "check": "npm run build && npm run check:skill", - "check:skill": "node scripts/check-skill.mjs" + "check": "npm run build && npm run check:skill && npm run check:docs-all", + "check:skill": "node scripts/check-skill.mjs", + "check:docs": "node scripts/check-docs.mjs", + "check:docs-search": "node scripts/check-docs-search.mjs", + "check:docs-all": "npm run check:docs && npm run docs:build && npm run check:docs-search", + "docs:dev": "vitepress dev site", + "docs:build": "vitepress build site", + "docs:preview": "vitepress preview site" }, "engines": { "node": ">=20" + }, + "devDependencies": { + "minisearch": "7.2.0", + "vitepress": "1.6.4" } } diff --git a/scripts/build-skill.mjs b/scripts/build-skill.mjs index d2d99bd..e9124c6 100644 --- a/scripts/build-skill.mjs +++ b/scripts/build-skill.mjs @@ -6,7 +6,7 @@ import skillConfig from '../src-skills/slm-design/skill.config.mjs'; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const sourceDir = path.join(repoRoot, 'src-skills', skillConfig.name); const sourcePath = path.join(sourceDir, skillConfig.source); -const docsDir = path.join(repoRoot, 'docs'); +const legacyDocsDir = path.join(repoRoot, 'old-docs'); const outputDir = path.join(repoRoot, 'skills', skillConfig.name); const includePattern = //g; @@ -50,8 +50,8 @@ if (!fs.existsSync(sourcePath)) { throw new Error(`Skill source not found: ${path.relative(repoRoot, sourcePath)}`); } -if (!fs.existsSync(docsDir)) { - throw new Error('Documentation directory not found: docs'); +if (!fs.existsSync(legacyDocsDir)) { + throw new Error('Legacy documentation directory not found: old-docs'); } const source = fs.readFileSync(sourcePath, 'utf8'); @@ -65,6 +65,6 @@ const output = [ fs.rmSync(outputDir, { recursive: true, force: true }); fs.mkdirSync(outputDir, { recursive: true }); fs.writeFileSync(path.join(outputDir, 'SKILL.md'), `${output}\n`); -fs.cpSync(docsDir, path.join(outputDir, 'reference'), { recursive: true }); +fs.cpSync(legacyDocsDir, path.join(outputDir, 'reference'), { recursive: true }); console.log(path.relative(repoRoot, path.join(outputDir, 'SKILL.md'))); diff --git a/scripts/check-docs-search.mjs b/scripts/check-docs-search.mjs new file mode 100644 index 0000000..d462844 --- /dev/null +++ b/scripts/check-docs-search.mjs @@ -0,0 +1,88 @@ +import { readFile, readdir } from 'node:fs/promises' +import path from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import MiniSearch from 'minisearch' +import { collectRules, RULE_SEARCH_OPTIONS } from './lib/specification.mjs' + +const repoRoot = fileURLToPath(new URL('../', import.meta.url)) +const specificationRoot = path.join(repoRoot, 'docs', 'ru', 'specification') +const distRoot = path.join(repoRoot, 'site', '.vitepress', 'dist') +const chunksDirectory = path.join(distRoot, 'assets', 'chunks') +const rules = await collectRules(specificationRoot) +const chunkNames = (await readdir(chunksDirectory)) + .filter((file) => file.startsWith('@localSearchIndexru.') && file.endsWith('.js')) + +if (chunkNames.length !== 1) { + throw new Error(`Expected one Russian search index chunk, found ${chunkNames.length}`) +} + +const searchData = ( + await import(`${pathToFileURL(path.join(chunksDirectory, chunkNames[0])).href}?t=${Date.now()}`) +).default +const searchIndex = MiniSearch.loadJSON(searchData, { + fields: ['title', 'titles', 'text'], + storeFields: ['title', 'titles'], +}) +const catalogHtml = await readFile( + path.join(distRoot, 'ru', 'specification', 'rules.html'), + 'utf8', +) +const htmlCache = new Map() + +function pageHtmlPath(pageHref) { + const route = pageHref.replace(/^\/ru\/specification\/?/, '') + return route.endsWith('/') || route === '' + ? path.join(distRoot, 'ru', 'specification', route, 'index.html') + : path.join(distRoot, 'ru', 'specification', `${route}.html`) +} + +for (const rule of rules) { + const expectedId = `/slm-design${rule.href}` + const firstResult = searchIndex.search(rule.id, RULE_SEARCH_OPTIONS)[0] + + if (firstResult?.id !== expectedId) { + throw new Error( + `Search for ${rule.id} returned ${firstResult?.id || 'nothing'} instead of ${expectedId}`, + ) + } + + if (!firstResult.title.startsWith(rule.id)) { + throw new Error(`Search title for ${rule.id} does not start with the exact rule ID`) + } + + if (firstResult.titles?.[0] !== 'Спецификация') { + throw new Error(`Search breadcrumb for ${rule.id} does not identify Specification`) + } + + const htmlPath = pageHtmlPath(rule.pageHref) + let html = htmlCache.get(htmlPath) + if (!html) { + html = await readFile(htmlPath, 'utf8') + htmlCache.set(htmlPath, html) + } + + if (!html.includes(`id="${rule.anchor}"`)) { + throw new Error(`Missing HTML anchor for ${rule.id} in ${rule.relativePath}`) + } + + if (!html.includes(`class="slm-rule__permalink" href="#${rule.anchor}"`)) { + throw new Error(`Missing permalink for ${rule.id} in ${rule.relativePath}`) + } + + if (!catalogHtml.includes(`>${rule.id}`)) { + throw new Error(`Rule catalog does not contain ${rule.id}`) + } +} + +const representativePage = await readFile( + path.join(distRoot, 'ru', 'specification', 'foundations.html'), + 'utf8', +) + +if (!representativePage.includes('class="doc-set-header"')) { + throw new Error('Specification sidebar does not contain the document-set header') +} + +console.log( + `Documentation search check passed: ${rules.length} exact rule queries, anchors, permalinks, and catalog entries.`, +) diff --git a/scripts/check-docs.mjs b/scripts/check-docs.mjs new file mode 100644 index 0000000..b5ecc69 --- /dev/null +++ b/scripts/check-docs.mjs @@ -0,0 +1,118 @@ +import { readFile } from 'node:fs/promises' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { + collectMarkdownFiles, + collectRules, + RULE_ID_PATTERN_SOURCE, + RULE_LEVEL_PATTERN_SOURCE, +} from './lib/specification.mjs' + +const specificationRoot = fileURLToPath( + new URL('../docs/ru/specification/', import.meta.url), +) + +const declarationPattern = new RegExp( + `^\\*\\*(${RULE_ID_PATTERN_SOURCE}) - (${RULE_LEVEL_PATTERN_SOURCE})\\.\\*\\*`, + 'gm', +) +const declarationLinePattern = new RegExp( + `^\\*\\*(${RULE_ID_PATTERN_SOURCE}) - (${RULE_LEVEL_PATTERN_SOURCE})\\.\\*\\*`, +) +const referencePattern = new RegExp(`\\b${RULE_ID_PATTERN_SOURCE}\\b`, 'g') +const legacyBaseReferencePattern = /\bSLM-(?!BASE-|ADV-|PRO-)[A-Z][A-Z0-9]*-\d{3}\b/g + +function lineNumberAt(content, index) { + return content.slice(0, index).split('\n').length +} + +const files = await collectMarkdownFiles(specificationRoot) +const registry = await collectRules(specificationRoot) +const declarations = new Map() +const references = [] +const errors = [] +const ruleCounts = { BASE: 0, ADV: 0, PRO: 0 } + +for (const file of files) { + const content = await readFile(file, 'utf8') + const relativePath = path.relative(specificationRoot, file).split(path.sep).join('/') + const lines = content.split('\n') + + for (const [index, line] of lines.entries()) { + if (line.startsWith('**SLM-') && !declarationLinePattern.test(line)) { + errors.push(`${relativePath}:${index + 1}: malformed rule declaration`) + } + } + + for (const match of content.matchAll(declarationPattern)) { + const id = match[1] + const location = `${relativePath}:${lineNumberAt(content, match.index)}` + const existingLocation = declarations.get(id) + + if (existingLocation) { + errors.push(`${location}: duplicate ${id}; first declared at ${existingLocation}`) + } else { + declarations.set(id, location) + ruleCounts[id.split('-')[1]] += 1 + } + + if (id.startsWith('SLM-BASE-') && relativePath.startsWith('modes/')) { + errors.push(`${location}: base rule ${id} cannot be declared in an overlay`) + } else if (id.startsWith('SLM-ADV-') && !relativePath.startsWith('modes/advanced/')) { + errors.push(`${location}: ${id} must be declared under modes/advanced`) + } else if (id.startsWith('SLM-PRO-') && !relativePath.startsWith('modes/pro/')) { + errors.push(`${location}: ${id} must be declared under modes/pro`) + } + } + + for (const match of content.matchAll(referencePattern)) { + references.push({ + id: match[0], + location: `${relativePath}:${lineNumberAt(content, match.index)}`, + }) + } + + for (const match of content.matchAll(legacyBaseReferencePattern)) { + errors.push( + `${relativePath}:${lineNumberAt(content, match.index)}: legacy base rule ID ${match[0]}`, + ) + } + + if (relativePath.startsWith('modes/advanced/') && /\bSLM-PRO-[A-Z-]+-\d{3}\b/.test(content)) { + errors.push(`${relativePath}: Advanced overlay references a Pro rule`) + } + + if (relativePath.startsWith('modes/pro/') && /\bSLM-ADV-[A-Z-]+-\d{3}\b/.test(content)) { + errors.push(`${relativePath}: Pro overlay references an Advanced rule`) + } +} + +for (const reference of references) { + if (!declarations.has(reference.id)) { + errors.push(`${reference.location}: unknown rule reference ${reference.id}`) + } +} + +if (registry.length !== declarations.size) { + errors.push( + `rule registry contains ${registry.length} records, but validator found ${declarations.size} declarations`, + ) +} + +for (const rule of registry) { + if (!declarations.has(rule.id)) { + errors.push(`${rule.relativePath}:${rule.line}: registry contains undeclared rule ${rule.id}`) + } +} + +if (errors.length > 0) { + console.error(`Documentation check failed with ${errors.length} error(s):`) + for (const error of errors) console.error(`- ${error}`) + process.exitCode = 1 +} else { + console.log( + `Documentation check passed: ${files.length} files, ${declarations.size} rules ` + + `(${ruleCounts.BASE} base, ${ruleCounts.ADV} advanced, ${ruleCounts.PRO} pro), ` + + `${references.length} rule occurrences.`, + ) +} diff --git a/scripts/lib/specification.mjs b/scripts/lib/specification.mjs new file mode 100644 index 0000000..74ff51a --- /dev/null +++ b/scripts/lib/specification.mjs @@ -0,0 +1,129 @@ +import { readdir, readFile } from 'node:fs/promises' +import path from 'node:path' + +export const RULE_ID_PATTERN_SOURCE = 'SLM-(?:BASE|ADV|PRO)-[A-Z][A-Z0-9]*-\\d{3}' +export const RULE_LEVEL_PATTERN_SOURCE = 'ОБЯЗАН|ЗАПРЕЩЕНО|СЛЕДУЕТ|МОЖЕТ' +export const RULE_SEARCH_OPTIONS = { + fuzzy: false, + prefix: true, + combineWith: 'AND', + boost: { title: 50, text: 2, titles: 1 }, +} + +const ruleIdPattern = /^SLM-(BASE|ADV|PRO)-([A-Z][A-Z0-9]*)-(\d{3})$/ +const declarationPattern = new RegExp( + `^\\*\\*(${RULE_ID_PATTERN_SOURCE}) - (${RULE_LEVEL_PATTERN_SOURCE})\\.\\*\\*\\s+(.+?)\\s*$`, +) +const rulesetOrder = { BASE: 0, ADV: 1, PRO: 2 } + +export async function collectMarkdownFiles(directory) { + const entries = await readdir(directory, { withFileTypes: true }) + const files = [] + + for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) { + const entryPath = path.join(directory, entry.name) + + if (entry.isDirectory()) { + files.push(...await collectMarkdownFiles(entryPath)) + } else if (entry.isFile() && entry.name.endsWith('.md')) { + files.push(entryPath) + } + } + + return files +} + +export function parseRuleId(id) { + const match = id.match(ruleIdPattern) + if (!match) return null + + return { + ruleset: match[1], + area: match[2], + number: Number(match[3]), + } +} + +export function parseRuleDeclaration(line) { + const match = line.match(declarationPattern) + if (!match) return null + + const parsedId = parseRuleId(match[1]) + if (!parsedId) return null + + return { + id: match[1], + ...parsedId, + level: match[2], + markdown: match[3], + text: stripInlineMarkdown(match[3]), + } +} + +export function stripInlineMarkdown(value) { + return value + .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1') + .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1') + .replace(/`([^`]+)`/g, '$1') + .replace(/<[^>]+>/g, '') + .replace(/[*_~]/g, '') + .replace(/\\([\\`*{}\[\]()#+.!_-])/g, '$1') + .replace(/\s+/g, ' ') + .trim() +} + +export function specificationPathToHref(relativePath, basePath = '/ru/specification/') { + let route = relativePath.split(path.sep).join('/') + + if (route === 'index.md') route = '' + else route = route.replace(/(?:^|\/)index\.md$/, '/').replace(/\.md$/, '') + + return `${basePath}${route}` +} + +export async function collectRules(specificationRoot, basePath = '/ru/specification/') { + const files = await collectMarkdownFiles(specificationRoot) + const rules = [] + + for (const file of files) { + const content = await readFile(file, 'utf8') + const relativePath = path.relative(specificationRoot, file).split(path.sep).join('/') + const headings = [] + + for (const [lineIndex, line] of content.split('\n').entries()) { + const heading = line.match(/^(#{1,6})\s+(.+?)\s*$/) + if (heading) { + const level = heading[1].length + headings.length = level + headings[level - 1] = stripInlineMarkdown(heading[2]) + continue + } + + const rule = parseRuleDeclaration(line) + if (!rule) continue + + const pageTitle = headings[0] || relativePath + const sectionTitles = headings.slice(1).filter(Boolean) + const pageHref = specificationPathToHref(relativePath, basePath) + + rules.push({ + ...rule, + anchor: rule.id.toLowerCase(), + href: `${pageHref}#${rule.id.toLowerCase()}`, + line: lineIndex + 1, + pageHref, + pageTitle, + relativePath, + sectionTitle: sectionTitles.at(-1) || pageTitle, + sectionTitles, + }) + } + } + + return rules.sort((left, right) => ( + rulesetOrder[left.ruleset] - rulesetOrder[right.ruleset] + || left.area.localeCompare(right.area) + || left.number - right.number + || left.id.localeCompare(right.id) + )) +} diff --git a/site/.vitepress/config.mts b/site/.vitepress/config.mts new file mode 100644 index 0000000..9acd2de --- /dev/null +++ b/site/.vitepress/config.mts @@ -0,0 +1,269 @@ +import { defineConfig } from 'vitepress' +import type MarkdownIt from 'markdown-it' +import { fileURLToPath } from 'node:url' +import { splitSearchSections } from './search.mts' +import { RULE_SEARCH_OPTIONS } from '../../scripts/lib/specification.mjs' + +const repositoryUrl = 'https://github.com/gromlab-ru/slm-design' +const viteConfigPath = fileURLToPath(new URL('../vite.config.mts', import.meta.url)) + +const specificationSidebar = [ + { + text: 'Начало', + items: [ + { text: 'Обзор спецификации', link: '/ru/specification/' }, + { text: 'Architecture modes', link: '/ru/specification/architecture-modes' }, + { text: 'Реестр правил', link: '/ru/specification/rules' }, + ], + }, + { + text: 'Base SLM', + collapsed: false, + items: [ + { + text: 'Основы', + items: [ + { text: 'Основные инварианты', link: '/ru/specification/foundations' }, + { text: 'Терминология', link: '/ru/specification/terminology' }, + { text: 'Архитектурная модель', link: '/ru/specification/architecture-model' }, + ], + }, + { + text: 'Слои', + collapsed: true, + items: [ + { text: 'Обзор', link: '/ru/specification/layers/' }, + { text: 'App', link: '/ru/specification/layers/app' }, + { text: 'Compositions', link: '/ru/specification/layers/compositions' }, + { text: 'Infra', link: '/ru/specification/layers/infra' }, + { text: 'UI', link: '/ru/specification/layers/ui' }, + { text: 'Shared', link: '/ru/specification/layers/shared' }, + ], + }, + { + text: 'Общие правила', + collapsed: true, + items: [ + { text: 'Модули и группы', link: '/ru/specification/modules-and-groups' }, + { text: 'Сегменты', link: '/ru/specification/segments' }, + { text: 'Public API и импорты', link: '/ru/specification/public-api-and-imports' }, + { text: 'State и data', link: '/ru/specification/state-and-data' }, + { text: 'Runtime и lifecycle', link: '/ru/specification/runtime-and-lifecycle' }, + { text: 'Тестирование', link: '/ru/specification/testing-and-conformance' }, + { text: 'Монорепозитории', link: '/ru/specification/monorepo' }, + ], + }, + ], + }, + { + text: 'Overlays', + collapsed: false, + items: [ + { + text: 'SLM Advanced', + collapsed: true, + items: [ + { text: 'Advanced overlay', link: '/ru/specification/modes/advanced/' }, + { text: 'Domains', link: '/ru/specification/modes/advanced/domains' }, + ], + }, + { + text: 'SLM Pro', + collapsed: true, + items: [ + { text: 'Pro overlay', link: '/ru/specification/modes/pro/' }, + { text: 'Domains', link: '/ru/specification/modes/pro/domains/' }, + { text: 'Business', link: '/ru/specification/modes/pro/domains/business' }, + { text: 'Framework surface', link: '/ru/specification/modes/pro/domains/framework' }, + { text: 'Ports и adapters', link: '/ru/specification/modes/pro/domains/ports-and-adapters' }, + { text: 'Client и server', link: '/ru/specification/modes/pro/domains/client-and-server' }, + { text: 'Cross-domain boundary', link: '/ru/specification/modes/pro/domains/cross-domain-boundary' }, + { text: 'Тестирование domains', link: '/ru/specification/modes/pro/domains/testing' }, + ], + }, + ], + }, +] + +function addRuleAnchors(md: MarkdownIt) { + const rulePattern = /^\*\*(SLM-(?:BASE|ADV|PRO)-[A-Z][A-Z-]*-\d{3}) - (ОБЯЗАН|ЗАПРЕЩЕНО|СЛЕДУЕТ|МОЖЕТ)\.\*\*/ + const kindByKeyword: Record = { + ОБЯЗАН: 'required', + ЗАПРЕЩЕНО: 'prohibited', + СЛЕДУЕТ: 'recommended', + МОЖЕТ: 'optional', + } + + md.core.ruler.after('inline', 'slm-rule-anchors', (state) => { + for (let index = 0; index < state.tokens.length - 1; index += 1) { + const paragraph = state.tokens[index] + const content = state.tokens[index + 1] + + if (paragraph.type !== 'paragraph_open' || content.type !== 'inline') continue + + const match = content.content.match(rulePattern) + if (!match) continue + + paragraph.attrSet('id', match[1].toLowerCase()) + paragraph.attrJoin('class', 'slm-rule') + paragraph.attrJoin('class', `slm-rule--${kindByKeyword[match[2]]}`) + + const strongCloseIndex = content.children?.findIndex((token) => token.type === 'strong_close') ?? -1 + if (strongCloseIndex < 0 || !content.children) continue + + const permalinkOpen = new state.Token('link_open', 'a', 1) + permalinkOpen.attrSet('class', 'slm-rule__permalink') + permalinkOpen.attrSet('href', `#${match[1].toLowerCase()}`) + permalinkOpen.attrSet('aria-label', `Ссылка на правило ${match[1]}`) + permalinkOpen.attrSet('title', `Ссылка на ${match[1]}`) + + const permalinkText = new state.Token('text', '', 0) + permalinkText.content = '#' + + const permalinkClose = new state.Token('link_close', 'a', -1) + content.children.splice( + strongCloseIndex + 1, + 0, + permalinkOpen, + permalinkText, + permalinkClose, + ) + } + }) +} + +export default defineConfig({ + srcDir: '../docs', + title: 'SLM Design', + description: 'Specification for explicit architecture boundaries in product applications', + lang: 'en-US', + base: '/slm-design/', + cleanUrls: true, + lastUpdated: true, + sitemap: { + hostname: 'https://gromlab-ru.github.io/slm-design/', + transformItems: (items) => items.map((item) => { + if (!item.links?.some((link) => link.url === 'ru/')) return item + + return { + ...item, + links: [ + { lang: 'x-default', url: '' }, + ...item.links.filter((link) => link.url !== ''), + ], + } + }), + }, + head: [ + ['link', { rel: 'icon', type: 'image/svg+xml', href: '/slm-design/logo.svg' }], + ['meta', { name: 'theme-color', content: '#d97706' }], + ], + markdown: { + config: addRuleAnchors, + }, + vite: { + configFile: viteConfigPath, + }, + themeConfig: { + logo: '/logo.svg', + siteTitle: 'SLM Design', + i18nRouting: false, + nav: [ + { text: 'Русская спецификация', link: '/ru/' }, + { text: 'English', link: '/en/' }, + ], + socialLinks: [{ icon: 'github', link: repositoryUrl }], + search: { + provider: 'local', + options: { + detailedView: false, + disableQueryPersistence: true, + miniSearch: { + searchOptions: RULE_SEARCH_OPTIONS, + _splitIntoSections: splitSearchSections, + }, + locales: { + ru: { + translations: { + button: { + buttonText: 'Поиск или rule ID', + buttonAriaLabel: 'Поиск по документации или rule ID', + }, + modal: { + displayDetails: 'Показать подробности', + resetButtonTitle: 'Сбросить поиск', + backButtonTitle: 'Закрыть поиск', + noResultsText: 'Ничего не найдено по запросу', + footer: { + selectText: 'выбрать', + navigateText: 'перейти', + closeText: 'закрыть', + }, + }, + }, + }, + }, + }, + }, + }, + locales: { + ru: { + label: 'Русский', + lang: 'ru-RU', + link: '/ru/', + title: 'SLM Design', + description: 'Спецификация архитектурных границ продуктовых приложений', + themeConfig: { + nav: [ + { text: 'Документация', link: '/ru/' }, + { text: 'Спецификация', link: '/ru/specification/' }, + ], + sidebar: { + '/ru/specification/': specificationSidebar, + }, + outline: { level: [2, 3], label: 'На этой странице' }, + editLink: { + pattern: `${repositoryUrl}/edit/master/docs/:path`, + text: 'Предложить изменение', + }, + lastUpdated: { + text: 'Обновлено', + formatOptions: { dateStyle: 'medium' }, + }, + docFooter: { + prev: 'Предыдущая страница', + next: 'Следующая страница', + }, + darkModeSwitchLabel: 'Оформление', + lightModeSwitchTitle: 'Светлая тема', + darkModeSwitchTitle: 'Тёмная тема', + sidebarMenuLabel: 'Содержание', + returnToTopLabel: 'Наверх', + langMenuLabel: 'Изменить язык', + skipToContentLabel: 'Перейти к содержанию', + footer: { + message: 'SLM Design 2.0 Draft', + copyright: 'Нормативная русская версия находится в статусе draft.', + }, + }, + }, + en: { + label: 'English', + lang: 'en-US', + link: '/en/', + title: 'SLM Design', + description: 'English translation placeholder for the SLM Design specification', + themeConfig: { + nav: [ + { text: 'English status', link: '/en/' }, + { text: 'Russian specification', link: '/ru/specification/' }, + ], + outline: { level: [2, 3], label: 'On this page' }, + footer: { + message: 'SLM Design 2.0 Draft', + copyright: 'The English edition is not normative yet.', + }, + }, + }, + }, +}) diff --git a/site/.vitepress/rules.data.mts b/site/.vitepress/rules.data.mts new file mode 100644 index 0000000..c0b7877 --- /dev/null +++ b/site/.vitepress/rules.data.mts @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { defineLoader } from 'vitepress' +import { collectRules } from '../../scripts/lib/specification.mjs' + +const specificationRoot = fileURLToPath( + new URL('../../docs/ru/specification/', import.meta.url), +) + +export default defineLoader({ + watch: '../../docs/ru/specification/**/*.md', + async load() { + return collectRules(specificationRoot) + }, +}) diff --git a/site/.vitepress/search.mts b/site/.vitepress/search.mts new file mode 100644 index 0000000..e11d099 --- /dev/null +++ b/site/.vitepress/search.mts @@ -0,0 +1,61 @@ +const headingPattern = /(.*?.*?<\/a>)<\/h\1>/gi +const headingContentPattern = /(.*?).*?<\/a>/i +const ruleBlockPattern = /

]*>([\s\S]*?)<\/p>/gi +const rulePermalinkPattern = /]*class="[^"]*\bslm-rule__permalink\b[^"]*"[^>]*>[\s\S]*?<\/a>/i + +function clearHtml(value: string) { + return value.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim() +} + +function makeRulesSearchable(html: string) { + return html.replace(ruleBlockPattern, (_block, anchor: string, innerHtml: string) => { + const strong = innerHtml.match(/([\s\S]*?)<\/strong>/i) + const label = clearHtml(strong?.[1] || anchor.toUpperCase()) + const bodyHtml = innerHtml + .replace(/[\s\S]*?<\/strong>/i, '') + .replace(rulePermalinkPattern, '') + .trim() + return `

${label}

${bodyHtml}

` + }) +} + +function* splitByHeadings(html: string) { + const parts = html.split(headingPattern) + parts.shift() + let parentTitles: string[] = [] + + for (let index = 0; index < parts.length; index += 3) { + const level = Number.parseInt(parts[index], 10) - 1 + const heading = headingContentPattern.exec(parts[index + 1]) + const title = clearHtml(heading?.[1] || '') + const anchor = heading?.[2] || '' + const text = clearHtml(parts[index + 2] || '') + + if (!title || !text) continue + + let titles = parentTitles.slice(0, level) + titles[level] = title + titles = titles.filter(Boolean) + + yield { anchor, titles, text } + + if (level === 0) parentTitles = [title] + else parentTitles[level] = title + } +} + +export function* splitSearchSections(file: string, html: string) { + const normalizedFile = file.replaceAll('\\', '/') + const documentTitle = normalizedFile.includes('/ru/specification/') + ? 'Спецификация' + : normalizedFile.includes('/ru/guide/') + ? 'Архитектурный гайд' + : null + + for (const section of splitByHeadings(makeRulesSearchable(html))) { + yield { + ...section, + titles: documentTitle ? [documentTitle, ...section.titles] : section.titles, + } + } +} diff --git a/site/.vitepress/theme/DocSetHeader.vue b/site/.vitepress/theme/DocSetHeader.vue new file mode 100644 index 0000000..659edb4 --- /dev/null +++ b/site/.vitepress/theme/DocSetHeader.vue @@ -0,0 +1,41 @@ + + + diff --git a/site/.vitepress/theme/Layout.vue b/site/.vitepress/theme/Layout.vue new file mode 100644 index 0000000..8aa0af5 --- /dev/null +++ b/site/.vitepress/theme/Layout.vue @@ -0,0 +1,29 @@ + + + diff --git a/site/.vitepress/theme/RuleCatalog.vue b/site/.vitepress/theme/RuleCatalog.vue new file mode 100644 index 0000000..8f0b2a1 --- /dev/null +++ b/site/.vitepress/theme/RuleCatalog.vue @@ -0,0 +1,271 @@ + + + + + diff --git a/site/.vitepress/theme/index.ts b/site/.vitepress/theme/index.ts new file mode 100644 index 0000000..499176f --- /dev/null +++ b/site/.vitepress/theme/index.ts @@ -0,0 +1,12 @@ +import DefaultTheme from 'vitepress/theme' +import Layout from './Layout.vue' +import RuleCatalog from './RuleCatalog.vue' +import './style.css' + +export default { + extends: DefaultTheme, + Layout, + enhanceApp({ app }) { + app.component('RuleCatalog', RuleCatalog) + }, +} diff --git a/site/.vitepress/theme/style.css b/site/.vitepress/theme/style.css new file mode 100644 index 0000000..c25a6ce --- /dev/null +++ b/site/.vitepress/theme/style.css @@ -0,0 +1,248 @@ +:root { + --vp-layout-top-height: 34px; + --vp-c-brand-1: #b45309; + --vp-c-brand-2: #d97706; + --vp-c-brand-3: #f59e0b; + --vp-c-brand-soft: rgba(217, 119, 6, 0.14); + --vp-c-bg: #f8f7f4; + --vp-c-bg-alt: #efede8; + --vp-c-bg-elv: #ffffff; + --vp-c-bg-soft: #f1efe9; + --vp-font-family-base: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + --vp-font-family-mono: "IBM Plex Mono", "SFMono-Regular", Consolas, "Liberation Mono", monospace; + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: linear-gradient(120deg, #92400e 5%, #d97706 50%, #f59e0b 95%); + --vp-home-hero-image-background-image: radial-gradient(circle, rgba(245, 158, 11, 0.32), transparent 68%); + --vp-home-hero-image-filter: blur(56px); +} + +.dark { + --vp-c-brand-1: #fbbf24; + --vp-c-brand-2: #f59e0b; + --vp-c-brand-3: #d97706; + --vp-c-brand-soft: rgba(245, 158, 11, 0.16); + --vp-c-bg: #111315; + --vp-c-bg-alt: #191c1f; + --vp-c-bg-elv: #202428; + --vp-c-bg-soft: #1c2023; +} + +html { + scroll-padding-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height) + 24px); +} + +.draft-banner { + position: fixed; + inset: 0 0 auto; + z-index: 50; + display: flex; + align-items: center; + justify-content: center; + gap: 9px; + height: var(--vp-layout-top-height); + padding: 0 16px; + overflow: hidden; + border-bottom: 1px solid rgba(120, 53, 15, 0.2); + background: #fef3c7; + color: #78350f; + font-family: var(--vp-font-family-mono); + font-size: 11px; + font-weight: 650; + letter-spacing: 0.055em; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dark .draft-banner { + border-bottom-color: rgba(251, 191, 36, 0.22); + background: #2b2114; + color: #fde68a; +} + +.draft-banner__mark { + width: 7px; + height: 7px; + flex: 0 0 auto; + border-radius: 50%; + background: #d97706; + box-shadow: 0 0 0 4px rgba(217, 119, 6, 0.13); +} + +.VPNavBarTitle .logo { + width: 25px; + height: 25px; +} + +.doc-set-header { + display: grid; + gap: 7px; + margin: 0 0 22px; + padding: 0 0 18px; + border-bottom: 1px solid var(--vp-c-divider); +} + +.doc-set-header__back { + width: fit-content; + color: var(--vp-c-text-3); + font-size: 11px; + font-weight: 650; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.doc-set-header__back::before { + content: '←'; + margin-right: 6px; +} + +.doc-set-header__title { + color: var(--vp-c-text-1); + font-size: 15px; + font-weight: 700; + line-height: 1.35; +} + +.doc-set-header__meta { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.doc-set-header__meta span { + padding: 3px 6px; + border-radius: 4px; + background: var(--vp-c-brand-soft); + color: var(--vp-c-brand-1); + font-family: var(--vp-font-family-mono); + font-size: 9px; + font-weight: 700; + letter-spacing: 0.045em; + text-transform: uppercase; +} + +.VPHome { + background-image: + linear-gradient(rgba(120, 113, 108, 0.07) 1px, transparent 1px), + linear-gradient(90deg, rgba(120, 113, 108, 0.07) 1px, transparent 1px); + background-position: center top; + background-size: 40px 40px; +} + +.VPHero .name, +.VPHero .text { + letter-spacing: -0.045em; +} + +.VPHero .tagline { + max-width: 660px; + font-size: 19px; + line-height: 1.65; +} + +.VPFeature { + border-color: rgba(120, 113, 108, 0.2) !important; + background: color-mix(in srgb, var(--vp-c-bg-soft) 84%, transparent) !important; + backdrop-filter: blur(8px); +} + +.vp-doc h1, +.vp-doc h2, +.vp-doc h3 { + letter-spacing: -0.025em; +} + +.vp-doc h1 { + font-size: clamp(2rem, 4vw, 2.65rem); +} + +.vp-doc table { + display: table; + width: 100%; +} + +.vp-doc .slm-rule { + position: relative; + margin: 18px 0; + padding: 15px 18px 15px 20px; + scroll-margin-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height) + 24px); + border: 1px solid var(--vp-c-divider); + border-left: 3px solid var(--vp-c-brand-2); + border-radius: 0 8px 8px 0; + background: var(--vp-c-bg-soft); + line-height: 1.7; +} + +.vp-doc .slm-rule strong:first-child { + color: var(--vp-c-text-1); + font-family: var(--vp-font-family-mono); + font-size: 0.89em; + letter-spacing: -0.015em; +} + +.vp-doc .slm-rule__permalink { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + margin: 0 2px 0 7px; + border-radius: 4px; + color: var(--vp-c-brand-1); + font-family: var(--vp-font-family-mono); + font-size: 13px; + font-weight: 700; + opacity: 0; + transition: background-color 0.15s, opacity 0.15s; + vertical-align: -1px; +} + +.vp-doc .slm-rule:hover .slm-rule__permalink, +.vp-doc .slm-rule__permalink:focus-visible { + opacity: 1; +} + +.vp-doc .slm-rule__permalink:hover, +.vp-doc .slm-rule__permalink:focus-visible { + background: var(--vp-c-brand-soft); +} + +.vp-doc .slm-rule--prohibited { + border-left-color: #dc2626; + background: rgba(220, 38, 38, 0.06); +} + +.vp-doc .slm-rule--recommended { + border-left-color: #2563eb; + background: rgba(37, 99, 235, 0.055); +} + +.vp-doc .slm-rule--optional { + border-left-color: #64748b; +} + +.vp-doc .slm-rule:target { + outline: 3px solid var(--vp-c-brand-soft); + outline-offset: 3px; +} + +@media (max-width: 640px) { + .draft-banner { + justify-content: flex-start; + padding-inline: 12px; + font-size: 10px; + letter-spacing: 0.02em; + } + + .VPHero .tagline { + font-size: 16px; + } + + .vp-doc .slm-rule { + margin-inline: -8px; + padding: 13px 14px; + } + + .vp-doc .slm-rule__permalink { + opacity: 1; + } +} diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..34c65be --- /dev/null +++ b/site/README.md @@ -0,0 +1,23 @@ +# SLM Design 2.0 Draft + +`site/` содержит VitePress-конфигурацию, тему и статические ресурсы сайта SLM Design. + +Новый publishable corpus находится в `docs/`. Действующая legacy-документация и reference текущего skill находятся в `old-docs/` до отдельного решения о принятии новой спецификации. + +## Точка входа + +[SLM Design Specification](../docs/ru/specification/index.md) + +Specification определяет base SLM и два независимых [architecture modes](../docs/ru/specification/architecture-modes.md): `SLM Advanced` и `SLM Pro`. Каждый mode является отдельным overlay непосредственно над base SLM. + +## Границы текущего этапа + +На этом этапе в `docs/ru/specification/` размещается только нормативная русская спецификация. Английский раздел зарезервирован под будущий перевод. Учебные материалы, руководства, примеры, справочники и agent skill будут проектироваться после стабилизации правил. + +## Локальный запуск + +```bash +npm run docs:dev +``` + +Production build создаётся командой `npm run docs:build`. diff --git a/site/public/logo.svg b/site/public/logo.svg new file mode 100644 index 0000000..4b00a54 --- /dev/null +++ b/site/public/logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/site/vite.config.mts b/site/vite.config.mts new file mode 100644 index 0000000..b155468 --- /dev/null +++ b/site/vite.config.mts @@ -0,0 +1,5 @@ +import { fileURLToPath } from 'node:url' + +export default { + publicDir: fileURLToPath(new URL('./public', import.meta.url)), +} diff --git a/skills/slm-design/reference/README.md b/skills/slm-design/reference/README.md index d6f92b8..0128dae 100644 --- a/skills/slm-design/reference/README.md +++ b/skills/slm-design/reference/README.md @@ -1,6 +1,6 @@ # SLM Design -`docs/` - файлы документации по SLM-архитектуре. +Этот каталог содержит действующую legacy-документацию по SLM-архитектуре. Используй эту документацию как источник истины для задач по SLM Design: слоям, модулям, сегментам, публичному API, business factory, composition modules и монорепозиториям. diff --git a/src-skills/slm-design/SKILL.md b/src-skills/slm-design/SKILL.md index 18b58cd..eeac23f 100644 --- a/src-skills/slm-design/SKILL.md +++ b/src-skills/slm-design/SKILL.md @@ -1,7 +1,7 @@ # SLM Design - + - + - +