From bf3f73708e7827a0d876f1e14f4f4be5877f4aef Mon Sep 17 00:00:00 2001 From: hided62 Date: Tue, 28 Jul 2026 23:59:52 +0000 Subject: [PATCH] docs: add rendered developer and player handbook --- .gitignore | 4 + README.md | 31 +- docs/.vitepress/config.mts | 61 ++ docs/developer/code-map.md | 83 ++ docs/developer/domain-and-classes.md | 106 ++ docs/developer/index.md | 29 + docs/developer/request-turn-persistence.md | 99 ++ docs/developer/system-architecture.md | 99 ++ docs/index.md | 32 + docs/reference-baseline.md | 39 + docs/user/command-catalog.generated.md | 188 ++++ docs/user/commands-and-timing.md | 98 ++ docs/user/index.md | 37 + docs/user/nation-and-features.md | 64 ++ docs/user/time-and-turns.md | 52 + package.json | 9 +- pnpm-lock.yaml | 1059 ++++++++++++++++++++ tools/docs/generate-command-reference.mjs | 129 +++ 18 files changed, 2216 insertions(+), 3 deletions(-) create mode 100644 docs/.vitepress/config.mts create mode 100644 docs/developer/code-map.md create mode 100644 docs/developer/domain-and-classes.md create mode 100644 docs/developer/index.md create mode 100644 docs/developer/request-turn-persistence.md create mode 100644 docs/developer/system-architecture.md create mode 100644 docs/index.md create mode 100644 docs/reference-baseline.md create mode 100644 docs/user/command-catalog.generated.md create mode 100644 docs/user/commands-and-timing.md create mode 100644 docs/user/index.md create mode 100644 docs/user/nation-and-features.md create mode 100644 docs/user/time-and-turns.md create mode 100644 tools/docs/generate-command-reference.mjs diff --git a/.gitignore b/.gitignore index 30ce65c..9fd5350 100644 --- a/.gitignore +++ b/.gitignore @@ -119,6 +119,10 @@ packages/infra/prisma/generated/ # Docusaurus cache and generated files .docusaurus +# VitePress documentation build output +docs/.vitepress/cache/ +docs/.vitepress/dist/ + # Serverless directories .serverless/ diff --git a/README.md b/README.md index 92d21f6..3d8b47c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ TypeScript로 호환 이관하는 pnpm 모노레포입니다. 기준 구현은 | `tools/integration-tests` | PostgreSQL/Redis 및 ref↔core 통합·차등 테스트 | | `tools/frontend-legacy-parity` | 실제 Chromium 기반 화면·상호작용 비교 | | `tools/legacy-db-migration` | 레거시 장기보존 데이터 CLI 이관 | -| `docs` | 런타임, 테스트, 배포 prefix와 운영 문서 | +| `tools/docs` | 플레이어 커맨드 문서 자동 생성 | +| `docs` | VitePress 핸드북과 런타임·테스트·운영 문서 | 런타임의 영속 입력은 PostgreSQL `input_event`가 담당합니다. game API가 요청을 기록하고 turn daemon이 claim한 뒤, 게임 상태·로그·예약 턴·결과와 @@ -60,6 +61,7 @@ battle simulation 등 해당 기능의 계약에만 사용하며 게임 mutation - Vue 3 + Pinia + Vue Router + Vite - PostgreSQL + Prisma, Redis - Vitest와 Playwright/Chromium +- VitePress 정적 HTML 문서 사이트 package manager 버전은 루트 `package.json`의 `packageManager`를 따릅니다. 현재 값은 `pnpm@11.17.0`입니다. 저장소는 Node `engines`를 고정하지 않으므로 @@ -144,6 +146,29 @@ pnpm --filter @sammo-ts/game-api dev pnpm --filter @sammo-ts/game-engine dev ``` +## 문서 사이트 + +개발자 아키텍처·파일 흐름·핵심 클래스와 플레이어 커맨드·시기별 가이드는 +[`docs/index.md`](docs/index.md)에서 시작합니다. VitePress 개발 서버와 정적 HTML +빌드는 다음 명령으로 실행합니다. + +```sh +pnpm docs:dev +pnpm docs:build +pnpm docs:preview +``` + +`docs:dev`와 `docs:build`는 먼저 `pnpm docs:generate`를 실행합니다. 이 단계는 +현재 장수·국가 명령 등록부와 각 `commandSpec`에서 +`docs/user/command-catalog.generated.md`를 다시 만듭니다. 생성 파일을 직접 +수정하지 말고 명령 정의나 생성기를 고쳐 주세요. 정적 HTML은 +`docs/.vitepress/dist`에 생성되며 Git에서 제외됩니다. + +핸드북이 조사한 코드 기준은 +[`docs/reference-baseline.md`](docs/reference-baseline.md)에 고정해 두었습니다. +기능 리팩터링 뒤에는 기준 commit부터 현재 commit까지의 diff를 대조하고 관련 +페이지, 생성 목록과 기준선을 같은 변경에서 갱신해 주세요. + ## DB schema와 migration Gateway는 기본적으로 PostgreSQL `public` schema를 사용하고, 게임은 @@ -204,6 +229,10 @@ worktree 흐름을 사용합니다. ## 핵심 문서 +- [core2026 핸드북](docs/index.md) +- [문서 기준 커밋](docs/reference-baseline.md) +- [개발자 핸드북](docs/developer/index.md) +- [플레이어 가이드](docs/user/index.md) - [테스트 정책](docs/testing-policy.md) - [테스트 suite 감사](docs/test-suite-audit.md) - [통합 테스트](docs/integration-tests.md) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 0000000..dcca0db --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,61 @@ +import { defineConfig } from 'vitepress'; + +export default defineConfig({ + lang: 'ko-KR', + title: 'core2026 핸드북', + description: 'SAM core2026 개발자 내부 문서와 플레이어 이용 가이드', + cleanUrls: true, + lastUpdated: true, + head: [['meta', { name: 'theme-color', content: '#6b3f22' }]], + themeConfig: { + siteTitle: 'core2026 핸드북', + nav: [ + { text: '개발자', link: '/developer/' }, + { text: '플레이어', link: '/user/' }, + { text: '기준 커밋', link: '/reference-baseline' }, + ], + sidebar: { + '/developer/': [ + { + text: '개발자 핸드북', + items: [ + { text: '시작하기', link: '/developer/' }, + { text: '시스템 아키텍처', link: '/developer/system-architecture' }, + { text: '요청·턴·저장 흐름', link: '/developer/request-turn-persistence' }, + { text: '도메인 로직과 핵심 클래스', link: '/developer/domain-and-classes' }, + { text: '파일 지도와 변경 절차', link: '/developer/code-map' }, + ], + }, + ], + '/user/': [ + { + text: '플레이어 가이드', + items: [ + { text: '시작하기', link: '/user/' }, + { text: '시간과 턴', link: '/user/time-and-turns' }, + { text: '커맨드와 실행 시기', link: '/user/commands-and-timing' }, + { text: '커맨드 전체 목록', link: '/user/command-catalog.generated' }, + { text: '국가 운영과 주요 기능', link: '/user/nation-and-features' }, + ], + }, + ], + }, + search: { + provider: 'local', + }, + outline: { + level: [2, 3], + label: '이 페이지에서', + }, + docFooter: { + prev: '이전', + next: '다음', + }, + lastUpdated: { + text: '마지막 변경', + }, + footer: { + message: '현재 구현을 설명하는 문서입니다. 기준 커밋과 검증 범위를 함께 확인해 주세요.', + }, + }, +}); diff --git a/docs/developer/code-map.md b/docs/developer/code-map.md new file mode 100644 index 0000000..3d57add --- /dev/null +++ b/docs/developer/code-map.md @@ -0,0 +1,83 @@ +# 파일 지도와 변경 절차 + +## 최상위 지도 + +```text +core2026/ +├─ app/ +│ ├─ gateway-frontend/ 계정·로비·관리 UI +│ ├─ gateway-api/ 인증·profile·operation·orchestrator +│ ├─ game-frontend/ profile 게임 SPA +│ ├─ game-api/ tRPC/SSE와 mutation 수신 +│ └─ game-engine/ turn daemon과 persistence orchestration +├─ packages/ +│ ├─ common/ 공통 타입·직렬화·RNG +│ ├─ logic/ 명령·constraint·전투·trigger·scenario +│ ├─ infra/ Prisma·PostgreSQL·Redis +│ └─ tools-scripts/ resource schema 도구 +├─ resources/ scenario·map·unitset·명령 profile +├─ tools/ +│ ├─ integration-tests/ DB/Redis 및 ref 차등 +│ ├─ frontend-legacy-parity/ 실제 Chromium 비교 +│ ├─ legacy-db-migration/ 장기보존 데이터 CLI +│ ├─ build-scripts/ profile resource 복사 기반 build 도구 +│ └─ docs/ 문서 생성 도구 +└─ docs/ VitePress 소스와 상세 설계 문서 +``` + +## 기능에서 파일로 + +| 기능 | 시작점 | 핵심 하위 경계 | +| -------------- | --------------------------------------------- | ------------------------------------- | +| 로그인·session | `gateway-api/src/router.ts` | `auth/*`, `account/router.ts`, Redis | +| profile 운영 | `gateway-api/src/adminRouter.ts` | `orchestrator/*`, gateway Prisma | +| 게임 인증 | `game-api/src/context.ts`, `router/auth` | session actor, profile·sanction | +| 메인 턴 입력 | `game-frontend/src/stores/mainDashboard.ts` | `router/turns`, `turns/*` | +| command 실행 | `game-engine/src/turn/reservedTurnHandler.ts` | `packages/logic/src/actions/turn` | +| 월간 lifecycle | `game-engine/src/turn/turnDaemon.ts` | `monthly*Handler.ts`, scenario events | +| 전투 | `actions/turn/general/che_출병.ts` | `packages/logic/src/war` | +| DB load/flush | `game-engine/src/turn/worldLoader.ts` | `databaseHooks.ts`, `packages/infra` | +| 공개/국가 정보 | `game-api/src/router/public`, `router/nation` | DTO와 redaction | +| 화면 parity | `game-frontend/src/views`, `styles` | `tools/frontend-legacy-parity` | + +## 변경 절차 + +### API나 화면 + +router의 input, auth procedure, transaction과 response를 먼저 정한 뒤 frontend 호출부와 오류·loading 상태를 +연결합니다. public prefix에서 direct navigation, asset, tRPC와 SSE URL을 확인합니다. UI를 바꾸면 실제 +Chromium에서 ref와 geometry·computed style·interaction을 비교합니다. + +### 도메인 로직 + +ref entry point부터 SQL·로그까지 호출 순서를 찾고, `packages/logic` 계산과 engine context/persistence를 함께 +수정합니다. unit test만으로 끝내지 않고 fixed seed, 전체 state, RNG trace, 실패 side effect와 가능한 +ref 차등을 확인합니다. + +### DB + +기존 migration을 고치지 않고 새 migration을 만듭니다. 빈 DB 전체 적용, 기존 DB 증분 적용, 재실행 no-op, +constraint/index와 runtime query, backup/restore 또는 rollback 경로를 확인합니다. + +## 문서 사이트 + +```sh +pnpm install +pnpm docs:generate +pnpm docs:dev +pnpm docs:build +pnpm docs:preview +``` + +`docs:dev`와 `docs:build`는 먼저 커맨드 목록을 생성합니다. 정적 결과는 `docs/.vitepress/dist`에 생기며 +Git에 포함하지 않습니다. 문서만 바꿔도 Prettier, 생성 결과의 clean diff, VitePress build와 내부 링크를 +검증해 주세요. + +## 리팩터링 체크포인트 + +- [문서 기준선](../reference-baseline.md)과 현재 commit의 diff를 먼저 봅니다. +- 파일 이동만 했는지 소유권·transaction·호출 순서까지 바뀌었는지 구분합니다. +- public API, DB schema, action key와 resource format은 내부 파일명보다 강한 계약입니다. +- `rg`로 이 페이지의 이전 경로가 남았는지 확인합니다. +- command key를 바꿨다면 저장된 예약 턴과 profile resource의 migration/호환 경로가 필요합니다. +- 문서의 광범위한 “완료” 표현은 관련 integration·ref 차등·Chromium 증거가 있을 때만 사용합니다. diff --git a/docs/developer/domain-and-classes.md b/docs/developer/domain-and-classes.md new file mode 100644 index 0000000..af22f3f --- /dev/null +++ b/docs/developer/domain-and-classes.md @@ -0,0 +1,106 @@ +# 도메인 로직과 핵심 클래스 + +## 핵심 entity + +`packages/logic/src/domain/entities.ts`가 HTTP나 Prisma row에 종속되지 않은 `General`, `City`, `Nation`, +`Troop`, diplomacy와 trigger 상태를 정의합니다. engine 전용 `TurnGeneral`, `TurnWorldState`, +`TurnEvent`는 `app/game-engine/src/turn/types.ts`에서 실행 시간·예약 턴·월간 상태를 더합니다. + +| entity | 핵심 책임 | +| ------- | -------------------------------------------------------------------------- | +| General | 능력치, 경험·공헌, 소속·도시·부대, 병력·훈련·사기, 자원, 특기·아이템, meta | +| City | 소유 국가, 규모, 인구·농업·상업·치안, 수비·성벽, 보급·전선 상태 | +| Nation | 수도, 국고·군량, 등급·국가 타입, 기술과 국가 meta | +| Troop | 부대장·구성원과 부대 상태 | +| World | 현재 연·월, 최근 턴 시각, scenario config/meta와 전체 entity collection | + +Prisma row를 곧바로 게임 규칙에 넘기지 않습니다. `worldLoader.ts`와 API의 row mapper가 DB 표현을 domain +표현으로 바꾸고, flush 계층이 반대 변환을 담당합니다. + +## 명령 정의 + +`GeneralActionDefinition`은 장수·국가 예약 명령이 공유하는 계약입니다. + +- `key`, `name`: 저장 key와 화면 표시명 +- `parseArgs`: 외부 입력을 실행 인자로 변환 +- `buildPermissionConstraints`: 예약 입력 자체를 허용할지 판단 +- `buildMinConstraints`: command table에서 현재 가능한지 사전 판단 +- `buildConstraints`: 실행 시점의 전체 조건 +- `getPreReqTurn`, `getPostReqTurn`: 연속 실행과 재사용 대기 +- `resolve`: domain state와 effect를 계산 + +각 파일의 `commandSpec`은 category, 인자 필요 여부, schema와 definition factory를 등록합니다. +`GENERAL_TURN_COMMAND_KEYS`, `NATION_TURN_COMMAND_KEYS`가 전체 key 집합이며, `TurnCommandProfile`이 +profile별 subset을 선택합니다. + +## Constraint 시스템 + +`packages/logic/src/constraints`는 “무엇이 필요한가”와 “현재 view가 무엇을 알고 있는가”를 분리합니다. +`ConstraintContext`에는 actor, city, nation, args, env와 평가 mode가 있고 `StateView`가 entity와 대상 +정보를 제공합니다. + +평가 결과는 다음 셋입니다. + +- `allow`: 현재 정보로 조건을 만족합니다. +- `deny`: 이유가 확정된 실패입니다. +- `unknown`: 대상 입력이나 추가 state가 없어 아직 판정할 수 없습니다. + +API command table은 `unknown`의 missing requirement가 대상 입력뿐이면 `needsInput`, 그 밖이면 +`unknown`으로 보여 줍니다. 예약 뒤 실제 실행에서는 전체 context로 다시 판단합니다. + +## 핵심 클래스와 조립 지점 + +### TurnDaemonLifecycle + +`app/game-engine/src/lifecycle/turnDaemonLifecycle.ts`에 있습니다. clock, control queue, hook과 run budget을 +조정하며 pause/resume/manual/scheduled run의 상태 전이를 소유합니다. + +### DatabaseTurnDaemonLease + +`app/game-engine/src/lifecycle/databaseTurnDaemonLease.ts`에 있습니다. profile별 단일 active owner와 fencing을 +관리합니다. daemon 계산이 맞아도 lease를 잃었다면 결과를 저장하면 안 됩니다. + +### InMemoryTurnWorld + +`app/game-engine/src/turn/inMemoryWorld.ts`에 있습니다. entity map, dirty/create/delete set, log, message, +event, checkpoint와 월 변경을 소유합니다. `peekDirtyState()`는 저장할 변경을 보여 주고 성공한 flush 뒤 +정리됩니다. + +### EngineStateManager + +`app/game-engine/src/turn/engineStateManager.ts`에 있습니다. world와 예약 턴 store 같은 mutable participant를 +등록하고 계산 단위의 capture/restore/transaction을 제공합니다. PostgreSQL transaction을 대신하지 않고 +실패한 계산의 메모리 rollback을 담당합니다. + +### InMemoryReservedTurnStore와 ReservedTurnHandler + +`reservedTurnStore.ts`는 장수 30칸·국가 12칸 예약 queue를 메모리에 유지합니다. +`reservedTurnHandler.ts`는 명령 loading, constraint, action context, AI fallback, progress/cooldown, +효과·로그와 queue rotation을 연결합니다. + +### GeneralActionPipeline과 trigger module + +`packages/logic/src/actions/engine.ts`, `triggers/*`는 명령 본체 전후의 특기·아이템·국가 특성 효과를 +일정한 우선순위로 적용합니다. 같은 module 목록이라도 실행 순서가 결과와 RNG 소비를 바꿀 수 있습니다. + +### WarEngine + +`packages/logic/src/war/engine.ts`가 전투 resolution을, `war/actions.ts`와 trigger module이 확장 효과를, +`war/aftermath.ts`가 피해·점령·외교·후속 state를 계산합니다. `che_출병.ts`가 map, unit set, diplomacy, +time, seed와 aftermath를 조립하는 실제 장수 명령 entry입니다. + +### GatewayOrchestrator + +`app/gateway-api/src/orchestrator/gatewayOrchestrator.ts`가 DB의 profile desired state를 process state에 +맞춥니다. `workspaceManager.ts`, `buildRunner.ts`, `seedProfileDatabase.ts`, `pm2ProcessManager.ts`가 +commit worktree 준비부터 build, seed, start/stop을 나눕니다. + +## 새 명령을 추가할 때 + +1. 가장 가까운 ref command의 constraint, 실행 순서, RNG, 로그와 DB side effect를 조사합니다. +2. `packages/logic/src/actions/turn/{general,nation}`에 definition과 `commandSpec`을 작성합니다. +3. 해당 `*_TURN_COMMAND_KEYS`와 필요한 `resources/turn-commands` profile에 key를 등록합니다. +4. 인자가 있으면 Zod schema와 `app/game-api/src/turns/commandInput.ts`의 화면 입력 field를 연결합니다. +5. engine action context가 대상 entity·map·unit set·시간·seed를 완전하게 공급하는지 확인합니다. +6. permission/min/full 실패, 성공, 연속 턴, cooldown과 persistence를 테스트합니다. +7. `pnpm docs:generate`로 플레이어 커맨드 목록을 갱신하고 ref 매핑 문서를 함께 수정합니다. diff --git a/docs/developer/index.md b/docs/developer/index.md new file mode 100644 index 0000000..160e776 --- /dev/null +++ b/docs/developer/index.md @@ -0,0 +1,29 @@ +# 개발자 핸드북 + +이 핸드북은 새 기능을 어디에 넣을지뿐 아니라 요청이 어떤 경계를 지나 상태로 남는지 설명합니다. 먼저 +[문서 기준선](../reference-baseline.md)을 확인하고, 변경 성격에 따라 다음 순서로 읽어 주세요. + +| 변경하려는 것 | 먼저 읽을 문서 | 주로 확인할 코드 | +| -------------------------- | ---------------------------------------------------- | -------------------------------------------- | +| 화면·라우팅·조회 API | [시스템 아키텍처](./system-architecture.md) | `app/*-frontend`, `app/*-api` | +| 턴 입력·게임 상태 mutation | [요청·턴·저장 흐름](./request-turn-persistence.md) | `app/game-api`, `app/game-engine` | +| 명령·전투·월간 로직 | [도메인 로직과 핵심 클래스](./domain-and-classes.md) | `packages/logic`, `app/game-engine/src/turn` | +| 새 파일 위치·검증 범위 | [파일 지도와 변경 절차](./code-map.md) | package manifest, test, docs | + +## 읽을 때 지켜야 할 경계 + +- `packages/logic`의 순수 계산과 `app/game-engine`의 scheduling·persistence orchestration을 구분합니다. +- game API가 mutation을 받는 것과 engine이 world mutation을 확정하는 것은 다른 단계입니다. +- PostgreSQL `input_event`가 내구성 있는 작업 경계이며 Redis는 realtime fan-out과 일부 보조 worker + 통신에 사용됩니다. +- 로그인한 사용자, 게임 장수, 국가 직책은 같은 개념이 아닙니다. actor와 소유권은 session에서 + 서버가 결정합니다. +- `resources/`의 scenario·map·unit set·turn-command profile이 런타임 구성을 바꿉니다. 기본 TypeScript + 목록만 보고 실제 profile을 단정하지 않습니다. +- ref 호환 변경은 결과뿐 아니라 판정·정렬·반올림·RNG 소비·로그·저장 순서를 비교합니다. + +## 기존 상세 문서와의 관계 + +이 핸드북은 탐색용 상위 지도입니다. 상세한 호환 근거와 테스트 절차는 `docs/architecture/*`, +`docs/integration-tests.md`, `docs/frontend-legacy-parity.md`에 유지합니다. 상위 작업공간의 +`../docs/ref-core2026-mapping.md`는 ref entry point와 core 구현의 end-to-end 대응 인덱스입니다. diff --git a/docs/developer/request-turn-persistence.md b/docs/developer/request-turn-persistence.md new file mode 100644 index 0000000..2571953 --- /dev/null +++ b/docs/developer/request-turn-persistence.md @@ -0,0 +1,99 @@ +# 요청·턴·저장 흐름 + +## 조회 요청 + +일반 query는 다음 경로를 따릅니다. + +```text +Vue view/store + -> tRPC client + -> game-api router + -> session actor + 입력 validation + -> Prisma query + -> 권한에 맞춘 DTO/redaction + -> 화면 상태 +``` + +조회는 engine의 in-memory object를 직접 공유하지 않습니다. 따라서 daemon이 transaction을 commit하기 전의 +중간 계산은 API query에 노출되지 않습니다. + +## API가 직접 끝내는 mutation + +예약 턴, 메시지처럼 API가 DB에서 완결할 수 있는 변경도 `input_event`를 사용합니다. + +1. `Idempotency-Key`와 tRPC path로 요청 identity를 만듭니다. +2. `app/game-api/src/inputEventBoundary.ts`가 중복·처리 상태를 확인합니다. +3. 같은 PostgreSQL transaction에서 대상 row와 input event 결과를 저장합니다. +4. commit 뒤 응답하고 필요한 realtime 갱신을 알립니다. + +동일 revision을 전제로 한 예약 턴 수정은 다른 탭이나 요청이 먼저 갱신했으면 충돌합니다. frontend는 최신 +목록을 다시 불러와 사용자의 변경을 덮어쓰지 않게 해야 합니다. + +## engine이 처리하는 mutation + +```text +game-api mutation + -> input_event PENDING + -> daemon claim (FOR UPDATE SKIP LOCKED) + -> lease/fencing 확인 + -> EngineStateManager savepoint + -> command/turn/monthly handler가 InMemoryTurnWorld 변경 + -> world + 예약 턴 + log + message + event 결과 flush + -> input_event COMPLETED를 같은 DB transaction으로 commit + -> commit 이후 realtime 신호 +``` + +계산이나 DB 쓰기가 실패하면 `EngineStateManager`가 등록된 in-memory participant를 savepoint로 되돌립니다. +DB transaction도 commit되지 않아 메모리와 DB의 부분 진행을 피합니다. lease를 잃은 worker는 stale 결과를 +commit할 수 없어야 합니다. + +## 한 tick의 처리 + +`TurnDaemonLifecycle`은 clock과 schedule에서 다음 실행 시점을 구합니다. 턴을 시작하면 +`InMemoryTurnProcessor`와 `InMemoryTurnWorld`가 `turnTime`, 그다음 `general.id` 순서로 실행 대상을 +결정합니다. checkpoint는 재시작 시 이미 처리한 동일 시점의 장수를 건너뛰는 기준입니다. + +장수 한 명의 예약 명령은 대략 다음 순서입니다. + +1. `InMemoryReservedTurnStore`에서 첫 예약 명령을 읽습니다. +2. 명령 key를 `GeneralTurnCommandLoader` 또는 `NationTurnCommandLoader`로 불러옵니다. +3. `actionContextBuilder`가 대상 도시·국가·장수, map, unit set, 외교, 시간과 RNG를 구성합니다. +4. permission/min/full constraint를 목적에 맞게 평가합니다. +5. 선행 턴이 있으면 진행 상태를 쌓고, 완성된 시점에 `resolve()`를 실행합니다. +6. effect와 직접 변경을 world에 반영하고 로그·메시지·후속 턴 시간을 기록합니다. +7. 실행된 queue를 당기고 끝에 기본 `휴식`을 채웁니다. + +예약 시 통과와 실행 시 성공은 같지 않습니다. 그 사이 자원, 도시 소유, 외교, 직책이 바뀔 수 있으므로 full +constraint는 실행 순간 다시 평가됩니다. + +## 월 변경 경계 + +`InMemoryTurnWorld.advanceMonth()`는 다음 순서를 보존합니다. + +1. 다음 연·월을 계산합니다. +2. `beforeMonthChanged` handler를 등록 순서대로 실행합니다. +3. world의 현재 연·월을 바꿉니다. +4. `onMonthChanged` handler를 등록 순서대로 실행합니다. +5. 연도가 바뀌었으면 `onYearChanged`를 실행합니다. + +`turnDaemon.ts`의 `composeCalendarHandlers()` 순서에는 월간 event, 수입, 연감, PRE_MONTH 상태 정리, +국가 명령, 국가 통계, 외교, 전쟁 설정, 방랑, 국가 수, 통일, 토너먼트, 경매, 전선 상태가 포함됩니다. +이 순서는 ref의 관찰 가능한 결과와 RNG·persistence에 영향을 주므로 리팩터링 시 단순 정렬하지 않습니다. + +## RNG 경계 + +게임 결과에 영향을 주는 난수는 `LiteHashDRBG`와 `RandUtil`을 사용합니다. seed에는 hidden seed와 +action/month/general 같은 context가 직렬화됩니다. main RNG의 소비 순서를 유지해야 하는 로직과 독립된 +재현 가능 substream을 써야 하는 fallback을 구분합니다. authoritative path에 `Math.random()`을 넣지 +않습니다. + +## 장애를 추적할 위치 + +| 증상 | 우선 확인 | +| ----------------------------- | --------------------------------------------------------------------- | +| 같은 mutation이 두 번 보임 | idempotency key, `input_event` 상태·attempt | +| 요청은 성공했지만 화면이 늦음 | DB commit 결과, Redis/SSE fan-out | +| daemon이 처리하지 않음 | profile gate, pause 상태, lease owner, PENDING claim | +| 재시작 뒤 일부 턴 반복 | checkpoint와 general turn ordering | +| DB와 메모리가 다름 | `EngineStateManager`, `databaseHooks`, flush 대상 누락 | +| ref와 결과가 다름 | constraint 순서, action context, RNG trace, rounding, log/effect 순서 | diff --git a/docs/developer/system-architecture.md b/docs/developer/system-architecture.md new file mode 100644 index 0000000..22cdbc0 --- /dev/null +++ b/docs/developer/system-architecture.md @@ -0,0 +1,99 @@ +# 시스템 아키텍처 + +## 런타임 구성 + +```text +브라우저 + ├─ /gateway/ ─ gateway-frontend ─ tRPC ─ gateway-api + │ ├─ PostgreSQL public schema + │ ├─ Redis session + │ └─ orchestrator ─ Git worktree / build / PM2 + │ + └─ /{profile}/ ─ game-frontend ─ tRPC/SSE ─ game-api + ├─ profile별 PostgreSQL schema + ├─ Redis realtime/battle worker + └─ input_event ─ game-engine + ├─ in-memory world + └─ transactional flush +``` + +Gateway는 계정·session·profile lifecycle을, game 계층은 한 profile의 플레이와 턴 진행을 소유합니다. +`/gateway`, `/che`, `/hwe` 같은 외부 prefix와 `/image/*`의 Caddy 소유권은 애플리케이션 밖의 배포 +계약입니다. + +## 애플리케이션 + +### gateway-frontend + +`app/gateway-frontend/src/main.ts`가 Vue 앱과 router를 시작합니다. 가입·로그인·계정·로비·관리자 화면은 +`src/views`에 있고, profile 선택 뒤 game frontend로 이동합니다. 브라우저에 보이는 `VITE_*` 값은 +공개 설정이며 secret이 아닙니다. + +### gateway-api + +`app/gateway-api/src/server.ts`와 `router.ts`가 HTTP/tRPC 경계입니다. + +- `auth/*`, `account/router.ts`: Kakao·로컬 계정, session 발급·폐기, 사용자 저장소 +- `lobby/profileStatusService.ts`: 사용자에게 보여 줄 profile 상태 +- `adminRouter.ts`, `adminAuth.ts`: 관리자 권한과 operation 입력 +- `orchestrator/*`: 원하는 profile 상태를 Git worktree, build, seed, PM2 프로세스에 반영 + +Gateway DB는 기본 `public` schema를 사용합니다. game profile DB를 직접 게임 로직의 source of truth로 +대체하지 않습니다. + +### game-frontend + +`app/game-frontend/src/main.ts`가 profile base path 아래 Vue SPA를 시작합니다. `src/views`가 공개 정보, +메인 턴 입력, 국가 운영, 경매·토너먼트·기록 화면을 나누고 `src/stores/mainDashboard.ts`가 메인 화면의 +query, 예약 턴 revision, mutation과 realtime refresh를 조정합니다. + +UI는 서버가 반환한 command table의 `available`, `blocked`, `needsInput`, `unknown` 상태를 사용합니다. +클라이언트가 장수 ID나 직책을 보냈다는 이유만으로 권한이 생기지 않습니다. + +### game-api + +`app/game-api/src/server.ts`와 `router.ts`가 query/mutation을 공개합니다. router는 기능 단위로 +`src/router/*`에 나뉩니다. + +- 읽기: world, public, directory, ranking, yearbook, dynasty 등은 권한·redaction을 거쳐 DB에서 조회합니다. +- 플레이: turns, join, nation, troop, diplomacy, messages, auction, betting, tournament 등이 있습니다. +- mutation: `inputEventBoundary.ts`가 idempotency와 PostgreSQL 작업 경계를 만듭니다. +- realtime: SSE와 Redis 알림은 commit 이후 화면 갱신 신호입니다. + +### game-engine + +`app/game-engine/src/index.ts`가 daemon entry point이고 `src/turn/turnDaemon.ts`가 profile resource, +world snapshot, command registry, calendar handler, persistence hook과 lease를 조립합니다. + +한 daemon owner가 `TurnDaemonLifecycle`을 통해 정해진 tick과 durable inbox를 처리합니다. 실제 장수·도시·국가 +상태는 `InMemoryTurnWorld`에서 계산하고, 성공한 작업만 `databaseHooks.ts`를 통해 PostgreSQL에 flush합니다. +lease/fencing은 오래된 daemon owner의 commit을 막습니다. + +## 공유 package + +| package | 책임 | 넣지 말아야 할 것 | +| ------------------------ | ------------------------------------------------------------------------------- | ---------------------------------- | +| `packages/common` | 공통 type, 직렬화, `LiteHashDRBG`, `RandUtil`, session/sanction 유틸리티 | profile DB orchestration | +| `packages/logic` | entity, constraint, command, battle, trigger, scenario parsing 같은 도메인 계산 | HTTP·Vue·Prisma transaction 소유권 | +| `packages/infra` | Prisma client, PostgreSQL·Redis connector, log와 turn-engine DB adapter | 게임 규칙 결정 | +| `packages/tools-scripts` | resource schema 생성·검증 | 런타임 요청 처리 | + +## 데이터와 구성 + +- `packages/infra/prisma/schema.gateway.prisma`: 계정·profile·operation 같은 gateway 모델 +- `packages/infra/prisma/schema.game.prisma`: profile별 world·general·city·nation·turn·event·log 모델 +- `resources/scenario`: 시작 연도, 상수, 월간 event 등 scenario 정의 +- `resources/map`, `resources/unitset`: 지형과 병종 정의 +- `resources/turn-commands`: profile별 허용 명령 목록 + +새 persistence field는 schema와 새 migration만으로 끝나지 않습니다. domain type, loader, in-memory dirty +tracking, transaction flush, reload 검증까지 연결해야 합니다. + +## 인증·권한 모델 + +Gateway session은 사용자 identity를, game token은 profile과 게임 역할을 전달합니다. game API는 session으로 +내 장수를 조회한 뒤 그 장수의 국가·직책·sanction과 대상 resource의 관계를 판단합니다. 공개 endpoint도 +비공개 국가 정보, 타 사용자 archive, 비밀 명령을 DTO에서 제거해야 합니다. + +권한 변경을 검증할 때는 무인증, 일반 사용자, 본인, 같은 국가, 다른 국가, NPC, 직책 보유자, sanction +적용자를 필요한 범위에서 나눕니다. 거부된 mutation은 world와 queue에 side effect를 남기지 않아야 합니다. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..e8b8f13 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,32 @@ +--- +layout: home + +hero: + name: core2026 핸드북 + text: 구현과 플레이를 한곳에서 설명합니다 + tagline: 현재 코드의 아키텍처·실행 흐름·핵심 클래스와 커맨드·시기별 이용 방법을 연결한 문서입니다. + actions: + - theme: brand + text: 개발자 핸드북 + link: /developer/ + - theme: alt + text: 플레이어 가이드 + link: /user/ + +features: + - title: 코드에서 실행까지 + details: frontend, tRPC, input_event, daemon, in-memory world와 PostgreSQL flush의 실제 경계를 따라갑니다. + - title: 커맨드와 시기 + details: 장수·국가 커맨드 전체 목록을 소스에서 생성하고, 언제 왜 실행 가능하거나 막히는지 설명합니다. + - title: 고정된 기준선 + details: 문서가 조사한 Git 기준 커밋과 재검증 지점을 밝혀 이후 리팩터링의 출발점을 남깁니다. +--- + +## 문서 성격 + +이 사이트는 `report/`의 작업 일지가 아니라 제품 저장소 안에서 계속 갱신하는 핸드북입니다. +개발자는 [시스템 아키텍처](./developer/system-architecture.md)부터, 플레이어는 +[시간과 턴](./user/time-and-turns.md)부터 읽으면 됩니다. + +문서의 사실관계는 [기준 커밋](./reference-baseline.md)의 코드에서 확인했습니다. 기능을 변경했다면 같은 +작업에서 관련 페이지와 자동 생성 커맨드 목록을 함께 갱신해 주세요. diff --git a/docs/reference-baseline.md b/docs/reference-baseline.md new file mode 100644 index 0000000..f474c87 --- /dev/null +++ b/docs/reference-baseline.md @@ -0,0 +1,39 @@ +# 문서 기준선 + +## 코드 기준 커밋 + +이 핸드북의 최초 전면 갱신은 다음 상태를 기준으로 조사했습니다. + +| 항목 | 값 | +| ----------- | ------------------------------------------ | +| 저장소 | `devsam/core2026.git` | +| 브랜치 | `main` | +| 기준 커밋 | `1181f6f4e03cbed77b1c40b6b572585f6e395a2c` | +| 기준 일자 | 2026-07-28 | +| 레거시 위치 | 형제 저장소 `../ref/sam`의 `devel` | + +기준 커밋은 “이 버전이 완성됐다”는 선언이 아니라 문장과 소스 경로를 다시 대조할 출발점입니다. 이후 +리팩터링에서 설명과 코드가 어긋나면 다음 순서로 갱신해 주세요. + +1. 이 페이지의 기준 커밋과 현재 `main` 사이의 변경 파일을 확인합니다. +2. 해당 문서가 가리키는 entry point, 호출 순서, transaction과 오류 경로를 다시 추적합니다. +3. 커맨드 등록부를 바꿨다면 `pnpm docs:generate`로 생성 페이지의 차이를 확인합니다. +4. `pnpm docs:build`로 링크와 정적 HTML 생성을 검증합니다. +5. 기준 커밋과 기준 일자를 현재 조사한 commit으로 바꾸고, 검증하지 못한 범위를 명시합니다. + +## 사실 수준 + +- 이 핸드북의 아키텍처·파일·클래스 설명은 기준 커밋의 정적 코드와 기존 검증 문서를 교차 확인한 + 결과입니다. +- `input_event` transaction, daemon lease, ref 차등, 실제 Chromium 같은 동작 증명은 각 테스트와 + 기존 상세 문서가 담당합니다. 이 핸드북 자체의 HTML 빌드가 제품 동작을 다시 증명하지는 않습니다. +- profile과 scenario가 명령 목록·상수·맵·병종을 바꿀 수 있으므로 플레이어 화면의 현재 가능 여부가 + 정적 표보다 우선입니다. + +## 상세 근거 문서 + +- [레거시 이관 제약](./architecture/rewrite-constraints.md) +- [턴 daemon lifecycle](./architecture/turn-daemon-lifecycle.md) +- [PostgreSQL schema](./architecture/postgres-schema.md) +- [프론트엔드 CSS 구조](./frontend-css-architecture.md) +- [레거시 화면 비교](./frontend-legacy-parity.md) diff --git a/docs/user/command-catalog.generated.md b/docs/user/command-catalog.generated.md new file mode 100644 index 0000000..63cd454 --- /dev/null +++ b/docs/user/command-catalog.generated.md @@ -0,0 +1,188 @@ +--- +title: 커맨드 전체 목록 +outline: deep +--- + + + +# 커맨드 전체 목록 + +이 페이지는 현재 소스의 명령 등록부와 각 `commandSpec`에서 자동 생성됩니다. +총 90개이며, profile의 `resources/turn-commands/*.json` 설정에 따라 실제 서버에서 일부가 제외될 수 +있습니다. “기본 실행 단위”는 정의에 고정된 선행 턴만 표시합니다. 자원, 신분, 도시, 외교, 시나리오 시점처럼 +실행 순간에 달라지는 조건은 [커맨드와 실행 시기](./commands-and-timing.md)를 확인해 주세요. + +## 장수 커맨드 + +등록된 명령은 55개입니다. + +### 전략 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------- | ------------------ | -------------- | -------------- | +| `che_거병` | 거병 | 없음 | 1턴 | +| `che_임관` | 임관 | 필요 | 1턴 | +| `che_랜덤임관` | 무작위 국가로 임관 | 없음 | 1턴 | +| `che_건국` | 건국 | 필요 | 1턴 | + +### 군사 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------- | --------- | -------------- | -------------- | +| `che_귀환` | 귀환 | 없음 | 1턴 | +| `che_훈련` | 훈련 | 없음 | 1턴 | +| `cr_맹훈련` | 맹훈련 | 없음 | 1턴 | +| `che_전투태세` | 전투태세 | 없음 | 1턴 | +| `che_단련` | 단련 | 없음 | 1턴 | +| `che_숙련전환` | 숙련전환 | 필요 | 1턴 | +| `che_사기진작` | 사기진작 | 없음 | 1턴 | +| `che_출병` | 출병 | 필요 | 1턴 | +| `che_집합` | 집합 | 없음 | 1턴 | +| `che_모병` | 모병 | 필요 | 1턴 | +| `che_소집해제` | 소집해제 | 없음 | 1턴 | +| `che_이동` | 이동 | 필요 | 1턴 | +| `che_방랑` | 방랑 | 없음 | 1턴 | +| `che_첩보` | 첩보 | 필요 | 1턴 | +| `che_파괴` | 파괴 | 필요 | 1턴 | +| `che_선동` | 선동 | 필요 | 1턴 | +| `che_탈취` | 탈취 | 필요 | 1턴 | +| `che_강행` | 강행 | 필요 | 1턴 | + +### 인사 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| ------------------ | ---------------- | -------------- | -------------- | +| `che_등용수락` | 등용수락 | 필요 | 1턴 | +| `che_장수대상임관` | 장수를 따라 임관 | 필요 | 1턴 | +| `che_인재탐색` | 인재탐색 | 없음 | 1턴 | +| `che_접경귀환` | 접경귀환 | 없음 | 1턴 | +| `che_하야` | 하야 | 없음 | 1턴 | +| `che_등용` | 등용 | 필요 | 1턴 | + +### 국가 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| ---------------- | ---------------- | -------------- | -------------- | +| `cr_건국` | 건국 | 필요 | 1턴 | +| `che_무작위건국` | 무작위 도시 건국 | 필요 | 1턴 | +| `che_선양` | 선양 | 필요 | 1턴 | +| `che_모반시도` | 모반시도 | 없음 | 1턴 | +| `che_증여` | 증여 | 필요 | 1턴 | +| `che_해산` | 해산 | 없음 | 1턴 | + +### 개인 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------------- | ---------------- | -------------- | -------------- | +| `che_요양` | 요양 | 없음 | 1턴 | +| `che_견문` | 견문 | 없음 | 1턴 | +| `che_장비매매` | 장비매매 | 필요 | 1턴 | +| `che_내정특기초기화` | 내정 특기 초기화 | 없음 | 1턴 | +| `che_전투특기초기화` | 전투 특기 초기화 | 없음 | 1턴 | +| `che_군량매매` | 군량매매 | 필요 | 1턴 | +| `che_은퇴` | 은퇴 | 없음 | 1턴 | +| `휴식` | 휴식 | 없음 | 1턴 | + +### 내정 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------- | --------- | -------------- | -------------- | +| `che_주민선정` | 주민 선정 | 없음 | 1턴 | +| `che_정착장려` | 정착 장려 | 없음 | 1턴 | +| `che_농지개간` | 농지 개간 | 없음 | 1턴 | +| `che_상업투자` | 상업 투자 | 없음 | 1턴 | +| `che_기술연구` | 기술 연구 | 없음 | 1턴 | +| `che_치안강화` | 치안 강화 | 없음 | 1턴 | +| `che_수비강화` | 수비 강화 | 없음 | 1턴 | +| `che_성벽보수` | 성벽 보수 | 없음 | 1턴 | +| `che_징병` | 징병 | 필요 | 1턴 | +| `che_물자조달` | 물자조달 | 없음 | 1턴 | +| `che_헌납` | 헌납 | 필요 | 1턴 | + +### 계략 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| ---------- | --------- | -------------- | -------------- | +| `che_화계` | 화계 | 필요 | 1턴 | + +### 특수 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| ------------- | --------- | -------------- | -------------- | +| `che_NPC능동` | NPC능동 | 필요 | 1턴 | + +## 국가 커맨드 + +등록된 명령은 35개입니다. + +### 휴식 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| ------- | --------- | -------------- | -------------- | +| `휴식` | 휴식 | 없음 | 1턴 | + +### 인사 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| ------------------ | -------------- | -------------- | -------------- | +| `che_포상` | 포상 | 필요 | 1턴 | +| `che_부대탈퇴지시` | 부대 탈퇴 지시 | 필요 | 1턴 | +| `che_발령` | 발령 | 필요 | 1턴 | +| `che_몰수` | 몰수 | 필요 | 1턴 | + +### 외교 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------------- | ---------------- | -------------- | -------------- | +| `che_선전포고` | 선전포고 | 필요 | 1턴 | +| `che_종전제의` | 종전 제의 | 필요 | 1턴 | +| `che_불가침제의` | 불가침 제의 | 필요 | 1턴 | +| `che_불가침파기제의` | 불가침 파기 제의 | 필요 | 1턴 | +| `che_이호경식` | 이호경식 | 필요 | 1턴 | +| `che_급습` | 급습 | 필요 | 1턴 | +| `che_물자원조` | 원조 | 필요 | 1턴 | + +### 전략 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------- | --------- | -------------- | -------------- | +| `che_의병모집` | 의병모집 | 없음 | 1턴 | +| `che_허보` | 허보 | 필요 | 1턴 | +| `che_필사즉생` | 필사즉생 | 없음 | 1턴 | +| `che_백성동원` | 백성동원 | 필요 | 1턴 | +| `che_수몰` | 수몰 | 필요 | 1턴 | +| `che_피장파장` | 피장파장 | 필요 | 1턴 | + +### 특수 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------------- | ------------- | -------------- | -------------- | +| `che_초토화` | 초토화 | 필요 | 1턴 | +| `cr_인구이동` | 인구이동 | 필요 | 1턴 | +| `event_원융노병연구` | 원융노병 연구 | 없음 | 24턴 연속 실행 | +| `event_화시병연구` | 화시병 연구 | 없음 | 12턴 연속 실행 | +| `event_음귀병연구` | 음귀병 연구 | 없음 | 12턴 연속 실행 | +| `event_대검병연구` | 대검병 연구 | 없음 | 12턴 연속 실행 | +| `event_화륜차연구` | 화륜차 연구 | 없음 | 24턴 연속 실행 | +| `event_산저병연구` | 산저병 연구 | 없음 | 12턴 연속 실행 | +| `event_극병연구` | 극병 연구 | 없음 | 24턴 연속 실행 | +| `event_상병연구` | 상병 연구 | 없음 | 24턴 연속 실행 | +| `event_무희연구` | 무희 연구 | 없음 | 24턴 연속 실행 | + +### 국가 + +| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 | +| -------------------- | ---------------- | -------------- | -------------- | +| `che_천도` | 천도 | 필요 | 1턴 | +| `che_국호변경` | 국호변경 | 필요 | 1턴 | +| `che_무작위수도이전` | 무작위 수도 이전 | 없음 | 1턴 | +| `che_국기변경` | 국기변경 | 필요 | 1턴 | +| `che_증축` | 증축 | 없음 | 1턴 | +| `che_감축` | 감축 | 없음 | 1턴 | + +## 생성 근거 + +- 등록 순서: `packages/logic/src/actions/turn/general/index.ts`, + `packages/logic/src/actions/turn/nation/index.ts` +- 표시명·분류·입력 여부·다중 턴: 각 명령 모듈의 `commandSpec`과 `ActionDefinition` +- 생성 명령: `pnpm docs:generate` diff --git a/docs/user/commands-and-timing.md b/docs/user/commands-and-timing.md new file mode 100644 index 0000000..d27cd45 --- /dev/null +++ b/docs/user/commands-and-timing.md @@ -0,0 +1,98 @@ +# 커맨드와 실행 시기 + +## 화면의 상태 표시 + +서버는 내 장수, 도시, 국가와 현재 시점으로 각 커맨드를 사전 평가합니다. + +| 표시 | 의미 | +| -------------- | ----------------------------------------------------------------------------- | +| 사용 가능 | 현재 알려진 조건을 만족합니다. 실행 시 다시 검사합니다. | +| 대상 선택 필요 | 도시·장수·국가·수량 같은 입력을 고르면 판정할 수 있습니다. | +| 사용 불가 | 현재 확정된 조건을 만족하지 않으며 이유가 함께 표시됩니다. | +| 정보 부족 | 사전 화면에 없는 실행 context가 필요합니다. 실제 실행 성공을 뜻하지 않습니다. | + +전체 key와 화면 이름은 [자동 생성 커맨드 목록](./command-catalog.generated.md)에 있습니다. + +## 시기별로 보는 장수 커맨드 + +### 재야·방랑 상태 + +임관, 무작위 임관, 장수를 따라 임관, 거병, 건국 계열이 중심입니다. 서버의 가입 방식이 “무작위 임관만” +이면 직접 국가를 고르는 임관·등용장 수락은 막힙니다. 초반에는 국가별 인원 제한과 건국 가능 도시·시점도 +적용될 수 있습니다. + +### 국가 소속·도시 주둔 + +농지 개간, 상업 투자, 치안 강화, 수비 강화, 성벽 보수, 정착 장려, 기술 연구, 헌납처럼 도시·국가를 +전제로 한 내정 커맨드를 사용할 수 있습니다. 대부분은 다음을 함께 검사합니다. + +- 재야나 방랑 국가가 아닌지 +- 내 장수가 실제 소속 도시를 점유하고 있는지 +- 해당 도시가 보급 상태인지 +- 목표 내정치가 이미 최대인지 +- 필요한 금·쌀과 능력·병력이 있는지 + +### 병력이 있을 때 + +훈련, 사기 진작, 단련, 전투 태세, 숙련 전환, 이동·집합·귀환, 출병을 검토할 수 있습니다. 출병은 병력과 +군량뿐 아니라 인접·경로, 대상 도시, 외교 관계, 보호 기간과 병종 조건을 실행 시점에 다시 확인합니다. + +### 대상이 필요한 행동 + +등용, 증여, 첩보, 화계, 파괴, 선동, 탈취, 이동·출병 등은 대상이나 수량을 선택해야 합니다. 목록에 대상을 +고를 수 없으면 이미 사라졌거나 현재 권한·공개 범위에서 선택할 수 없는 경우일 수 있습니다. + +### 국가 이탈·신분 변경 + +하야, 은퇴, 방랑, 선양, 모반 시도, 해산은 장수 또는 국가의 소속과 생애 상태를 크게 바꿉니다. 군주인지, +국가가 방랑 상태인지, 건국 직후인지, 후계 대상이 유효한지 같은 추가 조건이 있습니다. 로그와 확인 문구를 +읽고 가까운 후속 예턴도 함께 점검해 주세요. + +## 시기별로 보는 국가 커맨드 + +국가 예턴은 국가 운영 권한이 있을 때 보입니다. 많은 국가 명령은 군주만 실행할 수 있고, 일부 화면 운영 +기능은 직책별 권한을 따릅니다. + +### 상시 국가 운영 + +포상, 발령, 부대 탈퇴 지시, 몰수, 물자 원조, 국호·국기 변경, 천도, 증축·감축, 인구 이동 등이 있습니다. +국고·군량, 수도와 대상 도시, 장수·직책, 보급과 도시 규모 조건을 확인합니다. + +### 외교 상태에 따른 명령 + +선전포고는 기본 구현에서 scenario 시작 연도보다 적어도 1년 지난 뒤에 허용되며, 대상 국가와의 상태도 +맞아야 합니다. 종전·불가침·불가침 파기 제안은 현재 외교 관계와 최소 기간을 검사합니다. 상대가 제안을 +수락하는 즉시 행동은 일반 예약 국가 턴과 별도의 승인 흐름으로 처리됩니다. + +### 전략 커맨드 + +의병 모집, 허보, 필사즉생, 백성 동원, 이호경식, 수몰, 급습, 피장파장, 초토화는 군주 여부와 전략 커맨드 +재사용 가능 상태를 검사합니다. 명령마다 대상 도시·국가, 교전, 수도, 자원과 추가 상태가 다릅니다. +초반 보호 기간에는 의병 모집·몰수 등 일부 행동이 제한됩니다. + +### 특수 병종 연구 + +`event_*연구` 명령은 scenario가 해당 명령을 profile에 넣고 필요한 연구 flag가 아직 없을 때 사용할 수 +있습니다. 군주와 국고 조건을 검사하며 11턴 또는 23턴을 연속으로 쌓은 뒤 비용을 지불하고 병종 사용 +권한을 얻습니다. 소스의 선행 턴 값 11·23은 완료 턴을 포함하지 않으므로 화면에서 필요한 전체 실행은 +각각 12턴·24턴입니다. + +## 보호 기간과 연도 + +scenario의 `startYear`와 `openingPartYear`가 초반 제한의 기준입니다. 다음은 대표적인 동작이며 서버별 +상수에 따라 실제 연도가 달라집니다. + +- 출병은 예약 사전 판단과 실제 실행 모두 보호 기간을 고려합니다. +- 방랑, 의병 모집, 몰수 같은 일부 국가 변화도 초반에는 제한됩니다. +- 기술 연구의 허용 기술 단계와 임관 인원 제한은 경과 연도에 따라 넓어질 수 있습니다. +- 선전포고는 시작 직후가 아니라 상대 연도 1 이상에서 가능합니다. + +“몇 년 몇 월부터”를 고정 문구로 외우기보다 현재 서버의 시작 연도와 화면의 불가 이유를 확인해 주세요. + +## 실패했을 때 + +1. 메인 로그에서 실패 이유를 확인합니다. +2. 자원·병력, 소속 도시와 보급, 직책, 대상과 외교 상태를 다시 확인합니다. +3. 여러 턴 뒤 실행될 명령이면 앞선 예턴이 상태를 바꾸는지 살펴봅니다. +4. command table을 새로 불러와 대상 목록과 가능 상태를 갱신합니다. +5. 동일 요청을 반복 전송하지 말고 화면이 최신 revision을 받은 뒤 다시 저장해 주세요. diff --git a/docs/user/index.md b/docs/user/index.md new file mode 100644 index 0000000..56a9d53 --- /dev/null +++ b/docs/user/index.md @@ -0,0 +1,37 @@ +# 플레이어 가이드 + +core2026에서는 지금 누르는 버튼이 즉시 모든 결과를 만드는 것이 아니라, 많은 행동을 **예턴**에 넣고 내 +장수의 다음 턴에 실행합니다. 국가 커맨드도 같은 방식이지만 군주·직책과 국가 상태에 따라 이용 범위가 +달라집니다. + +## 처음 접속했을 때 + +1. Gateway에서 로그인하고 열려 있는 서버 profile을 선택합니다. +2. 게임에 장수가 없다면 장수 생성·참가 화면에서 이름, 능력치 등 필요한 정보를 정합니다. +3. 메인 화면에서 현재 도시·국가·자원·다음 턴 시각을 확인합니다. +4. 예턴 목록의 가까운 순서부터 실행할 커맨드를 지정합니다. +5. 국가에 소속됐다면 국가 정보·도시·장수·외교 화면에서 상황을 확인합니다. + +서버마다 scenario, 시작 연도, 턴 간격, 가입 방식, map, 병종과 허용 커맨드가 다를 수 있습니다. 이 문서는 +기본 구현을 설명하며 현재 화면의 가능/불가 표시와 운영 공지를 우선해 주세요. + +## 어디서 무엇을 하나요? + +| 메뉴 | 용도 | +| -------------------------- | ---------------------------------------------- | +| 메인 | 장수·국가 예턴, 현재 상태, 최근 기록 | +| 현재 도시·국가 정보 | 도시 내정치·보급·전선, 국가 자원·기술 | +| 국가 장수·인사 | 소속 장수와 직책, 권한이 있으면 인사·방침 설정 | +| 외교 | 국가 관계와 제안·교전 상태 확인 | +| 부대 | 부대 생성·가입·관리 | +| 경매·토너먼트·베팅 | 해당 기간에 열린 참가·입찰·예측 기능 | +| 게시판·메시지 | 공개/국가 소통과 개인 메시지 | +| 연감·명장·왕조·과거 플레이 | 현재·과거 기록 열람 | +| 내 정보·설정 | 표시·알림 등 개인 설정 | + +## 다음에 읽을 문서 + +- 예턴이 언제 실행되는지: [시간과 턴](./time-and-turns.md) +- 어떤 조건에서 커맨드를 쓸 수 있는지: [커맨드와 실행 시기](./commands-and-timing.md) +- 현재 소스에 등록된 전체 목록: [커맨드 전체 목록](./command-catalog.generated.md) +- 국가 직책과 부가 기능: [국가 운영과 주요 기능](./nation-and-features.md) diff --git a/docs/user/nation-and-features.md b/docs/user/nation-and-features.md new file mode 100644 index 0000000..45f6b76 --- /dev/null +++ b/docs/user/nation-and-features.md @@ -0,0 +1,64 @@ +# 국가 운영과 주요 기능 + +## 국가 직책과 정보 공개 + +국가에 소속됐다고 모든 국가 기능을 바꿀 수 있는 것은 아닙니다. 군주와 직책별 권한이 국가 예턴, 인사, +전략·재정 설정, 비밀 장수 정보와 정찰 차단 같은 기능에 적용됩니다. 같은 국가 장수에게만 보이는 정보도 +있고, 공개 목록에는 일부 값이 가려질 수 있습니다. + +내가 직접 URL이나 장수 번호를 입력해도 권한은 늘어나지 않습니다. 서버가 로그인 session에서 내 장수를 +찾아 국가·직책·대상 관계를 판단합니다. + +## 국가 운영 화면 + +| 화면 | 할 수 있는 일 | +| ----------- | --------------------------------------------- | +| 국가 정보 | 국호·국기, 수도, 국고·군량, 기술과 공지 확인 | +| 국가 도시 | 소유 도시의 내정·인구·방어·보급 상태 비교 | +| 국가 장수 | 소속 장수의 공개 정보와 직책 확인 | +| 인사 | 권한이 있으면 임명·해임, 추방, 봉록·권한 설정 | +| 전략·재정 | 세율·전쟁 차단·비밀 제한 등 허용된 국가 설정 | +| 비밀 장수 | 같은 국가 및 직책 권한에 맞춘 상세 정보 | +| 전투 본부 | 전선·출병 판단에 필요한 국가 전투 정보 | +| 정찰 메시지 | 정찰 관련 기록과 차단 상태 | + +변경 버튼이 보이더라도 실행 전에 직책과 대상이 다시 검사됩니다. 권한이 바뀐 직후에는 화면을 새로 불러와 +주세요. + +## 외교와 즉시 승인 + +선전포고 같은 일부 외교 행동은 국가 예턴으로 실행합니다. 종전·불가침·파기 제안은 제안 생성과 상대의 +수락이 나뉘며, 수락은 현재 관계와 권한을 다시 확인하는 즉시 action입니다. 제안 뒤 국가가 멸망하거나 +관계·직책이 바뀌면 수락할 수 없을 수 있습니다. + +## 부대 + +부대 화면에서는 생성, 가입, 탈퇴와 권한이 있는 관리 기능을 사용합니다. 부대 상태는 장수 예턴과 국가 +발령·탈퇴 지시에도 영향을 받을 수 있습니다. 출병 전에 부대장·소속과 예약된 명령을 함께 확인해 주세요. + +## 경매 + +경매는 열림·입찰·마감 단계가 있습니다. 입찰은 로그인한 내 장수와 자원을 기준으로 저장되고, 마감은 daemon +worker가 낙찰과 정산을 확정합니다. 이미 마감됐거나 자원이 바뀌면 이전에 보던 화면의 입찰 가능 상태가 +유효하지 않을 수 있습니다. + +## 토너먼트와 베팅 + +토너먼트는 등록·진행·종료와 보상 정산 lifecycle을 가집니다. 국가 베팅도 열림과 마감·정산 event가 +scenario 달력에 의해 발생합니다. 각 화면의 현재 상태와 마감 시각을 확인해 주세요. profile에서 event가 +열리지 않았다면 메뉴가 있어도 참여할 수 없습니다. + +## 게시판·메시지 + +공개 글, 국가 범위 글, 개인 메시지는 서로 공개 범위가 다릅니다. 작성자·수신자·국가 관계와 운영 권한에 +따라 조회·수정 범위가 결정됩니다. 민감한 외교나 국가 정보를 공개 게시판에 적지 말아 주세요. + +## 기록 + +- 연감은 월 경계의 world 상태를 보존합니다. +- 명장·순위·왕조 화면은 현재 또는 종료된 시즌의 집계 데이터를 보여 줍니다. +- 과거 플레이는 로그인한 사용자 소유 archive만 볼 수 있습니다. +- 다른 사용자의 archive나 국가 비밀 정보는 URL을 바꿔도 공개되지 않습니다. + +기록 화면의 값은 실시간 메인 상태와 갱신 시점이 다를 수 있습니다. 월 정산이나 시즌 종료 직후에는 해당 +기능의 최신 상태 표시를 함께 확인해 주세요. diff --git a/docs/user/time-and-turns.md b/docs/user/time-and-turns.md new file mode 100644 index 0000000..ff2efc0 --- /dev/null +++ b/docs/user/time-and-turns.md @@ -0,0 +1,52 @@ +# 시간과 턴 + +## 현실 시간과 게임 달력 + +서버는 profile의 턴 schedule에 따라 tick을 진행합니다. 한 tick마다 실행 시각이 된 장수의 명령을 처리하고, +게임 달력이 다음 달로 넘어갈 경계에서는 월간 정산과 event도 처리합니다. 실제 턴 간격은 서버 설정에 따라 +달라지므로 메인 화면의 다음 턴 시각을 확인해 주세요. + +게임 달력은 1월부터 12월까지 진행하고 12월 다음은 다음 해 1월입니다. 월이 바뀔 때 수입, 외교·전쟁 상태, +도시·국가 상태, 연감, scenario event 같은 여러 처리가 정해진 순서로 일어납니다. 그러므로 같은 커맨드도 +월 변경 직전과 직후에 자원이나 조건이 달라질 수 있습니다. + +## 예턴 + +- 장수 예턴은 최대 30칸입니다. +- 국가 예턴은 최대 12칸입니다. +- 위쪽에 가까운 명령부터 차례로 소비됩니다. +- 대상 도시·장수·국가, 병종, 수량 등이 필요한 커맨드는 입력값까지 저장됩니다. +- 비어 있는 칸과 실행 뒤 밀려난 끝 칸은 기본적으로 `휴식`으로 채워집니다. + +여러 칸에 한꺼번에 같은 명령을 넣거나 예턴을 앞뒤로 이동할 수 있습니다. 다른 탭에서 먼저 수정하면 revision +충돌이 날 수 있으므로 새 목록을 불러온 뒤 다시 적용해 주세요. + +## 예약할 수 있어도 실행에 실패할 수 있습니다 + +예턴을 넣는 시점과 실제 실행 시점 사이에 다음 상태가 바뀔 수 있습니다. + +- 가진 금·쌀과 병력 +- 도시의 소유 국가, 보급과 전선 상태 +- 내 소속 국가와 직책 +- 대상 장수·도시·국가의 존재와 외교 관계 +- 전쟁·불가침 기간과 전략 커맨드 재사용 상태 +- scenario 연도, 가입 방식, 기술 수준과 병종 개방 + +입력 화면은 현재 정보로 사전 판단하지만, 실제 턴에서는 전체 조건을 다시 확인합니다. 실패 이유는 명령 +로그에서 확인하고 다음 예턴을 조정해 주세요. + +## 여러 턴이 필요한 행동 + +일부 커맨드는 한 칸에서 즉시 끝나지 않고 선행 턴을 쌓습니다. 진행 중 조건이 깨지거나 명령이 바뀌면 결과가 +달라질 수 있습니다. 현재 기본 목록에서는 국가의 특수 병종 연구가 12턴 또는 24턴의 연속 실행을 요구합니다. +정확한 값은 [커맨드 전체 목록](./command-catalog.generated.md)의 “기본 실행 단위”에 표시됩니다. + +## 월 변경 전 확인할 것 + +- 금·쌀이 필요한 예턴을 계속 감당할 수 있는지 확인해 주세요. +- 공격·외교·전략 명령은 보호 기간, 교전 상태와 대상 도시가 그대로인지 확인해 주세요. +- 국가 운영자는 국고·군량, 직책과 외교 제안의 만료·수락 상태를 확인해 주세요. +- 경매·토너먼트·국가 베팅은 각 화면에 열린 기간과 마감 상태가 표시될 때만 입력해 주세요. + +월간 event는 scenario resource가 결정하므로 모든 서버가 같은 월에 같은 event를 실행한다고 가정하지 +말아 주세요. diff --git a/package.json b/package.json index bbe2aaa..04e4b71 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,11 @@ "check:legacy:nation": "node tools/compare-command-constraints.mjs --include '^Nation/' --check && node tools/compare-command-logs.mjs --include '^Nation/' --mode action --check", "check:legacy:general": "node tools/compare-command-constraints.mjs --include '^General/' --check && node tools/compare-command-logs.mjs --include '^General/' --mode action --check && node tools/compare-general-turn-contracts.mjs --check", "test:e2e:frontend-legacy": "playwright test --config tools/frontend-legacy-parity/playwright.config.mjs --tsconfig tools/frontend-legacy-parity/tsconfig.json", - "migrate:legacy": "pnpm --filter @sammo-ts/legacy-db-migration migrate" + "migrate:legacy": "pnpm --filter @sammo-ts/legacy-db-migration migrate", + "docs:generate": "node tools/docs/generate-command-reference.mjs", + "docs:dev": "pnpm docs:generate && vitepress dev docs", + "docs:build": "pnpm docs:generate && vitepress build docs", + "docs:preview": "vitepress preview docs" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -39,6 +43,7 @@ "turbo": "^2.10.6", "typescript": "6.0.2", "typescript-eslint": "^8.65.0", - "vue-eslint-parser": "^10.4.1" + "vue-eslint-parser": "^10.4.1", + "vitepress": "1.6.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58c64c6..cba4813 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: typescript-eslint: specifier: ^8.65.0 version: 8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.2) + vitepress: + specifier: 1.6.4 + version: 1.6.4(@algolia/client-search@5.56.0)(@types/node@26.1.1)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(typescript@6.0.2) vue-eslint-parser: specifier: ^10.4.1 version: 10.4.1(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0) @@ -510,6 +513,82 @@ importers: packages: + '@algolia/abtesting@1.22.0': + resolution: {integrity: sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g==} + engines: {node: '>= 14.0.0'} + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.56.0': + resolution: {integrity: sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.56.0': + resolution: {integrity: sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.56.0': + resolution: {integrity: sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.56.0': + resolution: {integrity: sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.56.0': + resolution: {integrity: sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.56.0': + resolution: {integrity: sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.56.0': + resolution: {integrity: sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.56.0': + resolution: {integrity: sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.56.0': + resolution: {integrity: sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.56.0': + resolution: {integrity: sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.56.0': + resolution: {integrity: sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.56.0': + resolution: {integrity: sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.56.0': + resolution: {integrity: sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q==} + engines: {node: '>= 14.0.0'} + '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} @@ -552,6 +631,29 @@ packages: '@chevrotain/utils@10.5.0': resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + 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 + '@electric-sql/pglite-socket@0.0.6': resolution: {integrity: sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==} hasBin: true @@ -584,102 +686,204 @@ packages: '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.27.2': resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.27.2': resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.27.2': resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.27.2': resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.27.2': resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.27.2': resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.2': resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.27.2': resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.27.2': resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.27.2': resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.27.2': resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.27.2': resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.27.2': resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.27.2': resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.27.2': resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.27.2': resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} engines: {node: '>=18'} @@ -692,6 +896,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.2': resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} engines: {node: '>=18'} @@ -704,6 +914,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.2': resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} engines: {node: '>=18'} @@ -716,24 +932,48 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.27.2': resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.27.2': resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.27.2': resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.27.2': resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} @@ -840,6 +1080,12 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify-json/simple-icons@1.2.92': + resolution: {integrity: sha512-hR0ozxR97t1dzWw+esoxFijZ15gagt7EIgF3CNifu2yICXhS7gnun4Y+j+odJQtNSl7wvqMdoLbViIShwe/fdw==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@img/colour@1.0.0': resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} @@ -1693,6 +1939,30 @@ packages: cpu: [x64] os: [win32] + '@shikijs/core@2.5.0': + resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + + '@shikijs/engine-javascript@2.5.0': + resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + + '@shikijs/engine-oniguruma@2.5.0': + resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + + '@shikijs/langs@2.5.0': + resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + + '@shikijs/themes@2.5.0': + resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + + '@shikijs/transformers@2.5.0': + resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + + '@shikijs/types@2.5.0': + resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2012,6 +2282,9 @@ packages: '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2021,6 +2294,9 @@ packages: '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} @@ -2036,6 +2312,9 @@ packages: '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/web-bluetooth@0.0.21': resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} @@ -2098,6 +2377,16 @@ packages: resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + '@vitejs/plugin-vue@6.0.3': resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2190,6 +2479,9 @@ packages: '@vue/shared@3.5.26': resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + '@vueuse/core@13.9.0': resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==} peerDependencies: @@ -2200,12 +2492,59 @@ packages: peerDependencies: vue: ^3.5.0 + '@vueuse/integrations@12.8.2': + resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} + 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 + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + '@vueuse/metadata@13.9.0': resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==} '@vueuse/metadata@14.1.0': resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==} + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + '@vueuse/shared@13.9.0': resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==} peerDependencies: @@ -2377,6 +2716,10 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + algoliasearch@5.56.0: + resolution: {integrity: sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q==} + engines: {node: '>= 14.0.0'} + alien-signals@3.1.2: resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==} @@ -2514,6 +2857,9 @@ packages: caniuse-lite@1.0.30001764: resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -2522,6 +2868,12 @@ packages: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + charm@0.1.2: resolution: {integrity: sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==} @@ -2554,6 +2906,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@2.15.1: resolution: {integrity: sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==} @@ -2672,6 +3027,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -2704,6 +3062,9 @@ packages: electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -2734,6 +3095,11 @@ packages: es-toolkit@1.43.0: resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -2948,6 +3314,9 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + focus-trap@7.8.0: + resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==} + follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -3042,6 +3411,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hono@4.10.6: resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} engines: {node: '>=16.9.0'} @@ -3055,6 +3430,9 @@ packages: hookable@6.1.1: resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -3310,13 +3688,34 @@ packages: resolution: {integrity: sha512-i053Kc0MgdUv/hu9mCyq67TYfPXFj3/MV8I7ZW5wvJNixIyXC0VztMPUjIVj/449nQo+BsxFD4Fdk/sA/uqKPQ==} engines: {node: '>= 20.0.0'} + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -3334,6 +3733,9 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minisearch@7.2.0: + resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} + mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} @@ -3415,6 +3817,9 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + oniguruma-to-es@3.1.1: + resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -3612,6 +4017,14 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} + preact@10.29.7: + resolution: {integrity: sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3650,6 +4063,9 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + prosemirror-changeset@2.3.1: resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} @@ -3764,6 +4180,15 @@ packages: resolution: {integrity: sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==} engines: {node: '>= 18'} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + regexp-to-ast@0.5.0: resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} @@ -3889,6 +4314,9 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} @@ -3928,6 +4356,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shiki@2.5.0: + resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -3967,6 +4398,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + speakingurl@14.0.1: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} @@ -3995,6 +4429,9 @@ packages: std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + superjson@2.2.6: resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} engines: {node: '>=16'} @@ -4017,6 +4454,9 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} + tailwindcss@4.1.18: resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} @@ -4066,6 +4506,9 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -4199,6 +4642,21 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + unrun@0.2.22: resolution: {integrity: sha512-vlQce4gTLNyCZxGylEQXGG+fSrrEFWiM/L8aghtp+t6j8xXh+lmsBtQJknG7ZSvv7P+/MRgbQtHWHBWk981uTg==} engines: {node: '>=20.19.0'} @@ -4233,6 +4691,12 @@ packages: resolution: {integrity: sha512-Njrh4U8UODGajoZ44QS2C/BsoEM9DTI/aCqY5swsizb+/ap0FamvnCMcZAxrR5+aoC0ZqkawEfpC/N2SBc+xeA==} engines: {node: '>=18.12.0'} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-tsconfig-paths@6.0.3: resolution: {integrity: sha512-7bL7FPX/DSviaZGYUKowWF1AiDVWjMjxNbE8lyaVGDezkedWqfGhlnQ4BZXre0ZN5P4kAgIJfAlgFDVyjrCIyg==} peerDependencies: @@ -4241,6 +4705,37 @@ packages: vite: optional: true + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + 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 + vite@7.3.0: resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4321,6 +4816,18 @@ packages: yaml: optional: true + vitepress@1.6.4: + resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + vitest@4.0.16: resolution: {integrity: sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4452,8 +4959,123 @@ packages: zod@4.3.5: resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: + '@algolia/abtesting@1.22.0': + 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 + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0) + '@algolia/client-search': 5.56.0 + algoliasearch: 5.56.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)': + dependencies: + '@algolia/client-search': 5.56.0 + algoliasearch: 5.56.0 + + '@algolia/client-abtesting@5.56.0': + 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 + + '@algolia/client-analytics@5.56.0': + 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 + + '@algolia/client-common@5.56.0': {} + + '@algolia/client-insights@5.56.0': + 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 + + '@algolia/client-personalization@5.56.0': + 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 + + '@algolia/client-query-suggestions@5.56.0': + 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 + + '@algolia/client-search@5.56.0': + 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 + + '@algolia/ingestion@1.56.0': + 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 + + '@algolia/monitoring@1.56.0': + 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 + + '@algolia/recommend@5.56.0': + 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 + + '@algolia/requester-browser-xhr@5.56.0': + dependencies: + '@algolia/client-common': 5.56.0 + + '@algolia/requester-fetch@5.56.0': + dependencies: + '@algolia/client-common': 5.56.0 + + '@algolia/requester-node-http@5.56.0': + dependencies: + '@algolia/client-common': 5.56.0 + '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -4499,6 +5121,31 @@ snapshots: '@chevrotain/utils@10.5.0': {} + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3) + preact: 10.29.7 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - preact-render-to-string + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.56.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + '@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)': dependencies: '@electric-sql/pglite': 0.3.2 @@ -4541,81 +5188,150 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.21.5': + optional: true + '@esbuild/aix-ppc64@0.27.2': optional: true + '@esbuild/android-arm64@0.21.5': + optional: true + '@esbuild/android-arm64@0.27.2': optional: true + '@esbuild/android-arm@0.21.5': + optional: true + '@esbuild/android-arm@0.27.2': optional: true + '@esbuild/android-x64@0.21.5': + optional: true + '@esbuild/android-x64@0.27.2': optional: true + '@esbuild/darwin-arm64@0.21.5': + optional: true + '@esbuild/darwin-arm64@0.27.2': optional: true + '@esbuild/darwin-x64@0.21.5': + optional: true + '@esbuild/darwin-x64@0.27.2': optional: true + '@esbuild/freebsd-arm64@0.21.5': + optional: true + '@esbuild/freebsd-arm64@0.27.2': optional: true + '@esbuild/freebsd-x64@0.21.5': + optional: true + '@esbuild/freebsd-x64@0.27.2': optional: true + '@esbuild/linux-arm64@0.21.5': + optional: true + '@esbuild/linux-arm64@0.27.2': optional: true + '@esbuild/linux-arm@0.21.5': + optional: true + '@esbuild/linux-arm@0.27.2': optional: true + '@esbuild/linux-ia32@0.21.5': + optional: true + '@esbuild/linux-ia32@0.27.2': optional: true + '@esbuild/linux-loong64@0.21.5': + optional: true + '@esbuild/linux-loong64@0.27.2': optional: true + '@esbuild/linux-mips64el@0.21.5': + optional: true + '@esbuild/linux-mips64el@0.27.2': optional: true + '@esbuild/linux-ppc64@0.21.5': + optional: true + '@esbuild/linux-ppc64@0.27.2': optional: true + '@esbuild/linux-riscv64@0.21.5': + optional: true + '@esbuild/linux-riscv64@0.27.2': optional: true + '@esbuild/linux-s390x@0.21.5': + optional: true + '@esbuild/linux-s390x@0.27.2': optional: true + '@esbuild/linux-x64@0.21.5': + optional: true + '@esbuild/linux-x64@0.27.2': optional: true '@esbuild/netbsd-arm64@0.27.2': optional: true + '@esbuild/netbsd-x64@0.21.5': + optional: true + '@esbuild/netbsd-x64@0.27.2': optional: true '@esbuild/openbsd-arm64@0.27.2': optional: true + '@esbuild/openbsd-x64@0.21.5': + optional: true + '@esbuild/openbsd-x64@0.27.2': optional: true '@esbuild/openharmony-arm64@0.27.2': optional: true + '@esbuild/sunos-x64@0.21.5': + optional: true + '@esbuild/sunos-x64@0.27.2': optional: true + '@esbuild/win32-arm64@0.21.5': + optional: true + '@esbuild/win32-arm64@0.27.2': optional: true + '@esbuild/win32-ia32@0.21.5': + optional: true + '@esbuild/win32-ia32@0.27.2': optional: true + '@esbuild/win32-x64@0.21.5': + optional: true + '@esbuild/win32-x64@0.27.2': optional: true @@ -4726,6 +5442,12 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify-json/simple-icons@1.2.92': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + '@img/colour@1.0.0': {} '@img/sharp-darwin-arm64@0.34.5': @@ -5331,6 +6053,46 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.55.1': optional: true + '@shikijs/core@2.5.0': + 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.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 3.1.1 + + '@shikijs/engine-oniguruma@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/themes@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/transformers@2.5.0': + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/types': 2.5.0 + + '@shikijs/types@2.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + '@standard-schema/spec@1.1.0': {} '@tailwindcss/node@4.1.18': @@ -5638,6 +6400,10 @@ snapshots: '@types/geojson@7946.0.16': {} + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} '@types/linkify-it@5.0.0': {} @@ -5647,6 +6413,10 @@ snapshots: '@types/linkify-it': 5.0.0 '@types/mdurl': 2.0.0 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/mdurl@2.0.0': {} '@types/node@25.0.3': @@ -5667,6 +6437,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/unist@3.0.3': {} + '@types/web-bluetooth@0.0.21': {} '@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.2))(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.2)': @@ -5760,6 +6532,13 @@ snapshots: '@typescript-eslint/types': 8.65.0 eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.3': {} + + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@26.1.1)(lightningcss@1.30.2))(vue@3.5.26(typescript@6.0.2))': + dependencies: + vite: 5.4.21(@types/node@26.1.1)(lightningcss@1.30.2) + vue: 3.5.26(typescript@6.0.2) + '@vitejs/plugin-vue@6.0.3(vite@7.3.0(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))(vue@3.5.26(typescript@6.0.2))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.53 @@ -5917,6 +6696,15 @@ snapshots: '@vue/shared@3.5.26': {} + '@vueuse/core@12.8.2(typescript@6.0.2)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2(typescript@6.0.2) + vue: 3.5.26(typescript@6.0.2) + transitivePeerDependencies: + - typescript + '@vueuse/core@13.9.0(vue@3.5.26(typescript@6.0.2))': dependencies: '@types/web-bluetooth': 0.0.21 @@ -5931,10 +6719,28 @@ snapshots: '@vueuse/shared': 14.1.0(vue@3.5.26(typescript@6.0.2)) vue: 3.5.26(typescript@6.0.2) + '@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@6.0.2)': + dependencies: + '@vueuse/core': 12.8.2(typescript@6.0.2) + '@vueuse/shared': 12.8.2(typescript@6.0.2) + vue: 3.5.26(typescript@6.0.2) + optionalDependencies: + focus-trap: 7.8.0 + transitivePeerDependencies: + - typescript + + '@vueuse/metadata@12.8.2': {} + '@vueuse/metadata@13.9.0': {} '@vueuse/metadata@14.1.0': {} + '@vueuse/shared@12.8.2(typescript@6.0.2)': + dependencies: + vue: 3.5.26(typescript@6.0.2) + transitivePeerDependencies: + - typescript + '@vueuse/shared@13.9.0(vue@3.5.26(typescript@6.0.2))': dependencies: vue: 3.5.26(typescript@6.0.2) @@ -6045,6 +6851,23 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + algoliasearch@5.56.0: + 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 + alien-signals@3.1.2: {} amp-message@0.1.2: @@ -6162,6 +6985,8 @@ snapshots: caniuse-lite@1.0.30001764: {} + ccount@2.0.1: {} + chai@6.2.2: {} chalk@3.0.0: @@ -6169,6 +6994,10 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + charm@0.1.2: {} chevrotain@10.5.0: @@ -6212,6 +7041,8 @@ snapshots: color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + commander@2.15.1: {} confbox@0.2.2: {} @@ -6292,6 +7123,10 @@ snapshots: detect-libc@2.1.2: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + dotenv@16.6.1: {} dotenv@17.2.3: {} @@ -6307,6 +7142,8 @@ snapshots: electron-to-chromium@1.5.267: {} + emoji-regex-xs@1.0.0: {} + empathic@2.0.0: {} empathic@2.0.1: {} @@ -6328,6 +7165,32 @@ snapshots: es-toolkit@1.43.0: {} + esbuild@0.21.5: + 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 + esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -6591,6 +7454,10 @@ snapshots: flatted@3.3.3: {} + focus-trap@7.8.0: + dependencies: + tabbable: 6.5.0 + follow-redirects@1.15.11(debug@4.3.7(supports-color@7.2.0)): optionalDependencies: debug: 4.3.7(supports-color@7.2.0) @@ -6675,6 +7542,24 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + hono@4.10.6: {} hookable@5.5.3: {} @@ -6683,6 +7568,8 @@ snapshots: hookable@6.1.1: {} + html-void-elements@3.0.0: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -6891,6 +7778,8 @@ snapshots: iconv-lite: 0.7.3 lru-cache: 11.5.2 + mark.js@8.11.1: {} + markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -6900,8 +7789,37 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.3 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + mdurl@2.0.0: {} + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + mime@3.0.0: {} minimatch@10.1.1: @@ -6914,6 +7832,8 @@ snapshots: minipass@7.1.2: {} + minisearch@7.2.0: {} + mitt@3.0.1: {} mkdirp@1.0.4: {} @@ -6987,6 +7907,12 @@ snapshots: on-exit-leak-free@2.1.2: {} + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.1.0 + regex-recursion: 6.0.2 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -7236,6 +8162,8 @@ snapshots: postgres@3.4.7: {} + preact@10.29.7: {} + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.1: @@ -7274,6 +8202,8 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 + property-information@7.2.0: {} + prosemirror-changeset@2.3.1: dependencies: prosemirror-transform: 1.11.0 @@ -7434,6 +8364,16 @@ snapshots: '@redis/search': 5.10.0(@redis/client@5.10.0) '@redis/time-series': 5.10.0(@redis/client@5.10.0) + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + regexp-to-ast@0.5.0: {} remeda@2.21.3: @@ -7638,6 +8578,8 @@ snapshots: scheduler@0.27.0: {} + search-insights@2.17.3: {} + secure-json-parse@4.1.0: {} semver@7.5.4: @@ -7691,6 +8633,17 @@ snapshots: shebang-regex@3.0.0: {} + shiki@2.5.0: + 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.5 + shimmer@1.2.1: {} siginfo@2.0.0: {} @@ -7727,6 +8680,8 @@ snapshots: source-map@0.6.1: {} + space-separated-tokens@2.0.2: {} + speakingurl@14.0.1: {} split2@4.2.0: {} @@ -7743,6 +8698,11 @@ snapshots: std-env@3.9.0: {} + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + superjson@2.2.6: dependencies: copy-anything: 4.0.5 @@ -7760,6 +8720,8 @@ snapshots: systeminformation@5.28.10: optional: true + tabbable@6.5.0: {} + tailwindcss@4.1.18: {} tapable@2.3.0: {} @@ -7796,6 +8758,8 @@ snapshots: tree-kill@1.2.2: {} + trim-lines@3.0.1: {} + ts-api-utils@2.5.0(typescript@6.0.2): dependencies: typescript: 6.0.2 @@ -7922,6 +8886,29 @@ snapshots: undici-types@8.3.0: {} + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + unrun@0.2.22(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(synckit@0.11.13): dependencies: rolldown: 1.0.0-beta.58(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) @@ -7949,6 +8936,16 @@ snapshots: verkit@0.3.0: {} + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite-tsconfig-paths@6.0.3(supports-color@7.2.0)(typescript@6.0.2)(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): dependencies: debug: 4.4.3(supports-color@7.2.0) @@ -7960,6 +8957,16 @@ snapshots: - supports-color - typescript + vite@5.4.21(@types/node@26.1.1)(lightningcss@1.30.2): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.55.1 + optionalDependencies: + '@types/node': 26.1.1 + fsevents: 2.3.3 + lightningcss: 1.30.2 + vite@7.3.0(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: esbuild: 0.27.2 @@ -7990,6 +8997,56 @@ snapshots: lightningcss: 1.30.2 tsx: 4.21.0 + vitepress@1.6.4(@algolia/client-search@5.56.0)(@types/node@26.1.1)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(typescript@6.0.2): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.92 + '@shikijs/core': 2.5.0 + '@shikijs/transformers': 2.5.0 + '@shikijs/types': 2.5.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@26.1.1)(lightningcss@1.30.2))(vue@3.5.26(typescript@6.0.2)) + '@vue/devtools-api': 7.7.9 + '@vue/shared': 3.5.26 + '@vueuse/core': 12.8.2(typescript@6.0.2) + '@vueuse/integrations': 12.8.2(focus-trap@7.8.0)(typescript@6.0.2) + focus-trap: 7.8.0 + mark.js: 8.11.1 + minisearch: 7.2.0 + shiki: 2.5.0 + vite: 5.4.21(@types/node@26.1.1)(lightningcss@1.30.2) + vue: 3.5.26(typescript@6.0.2) + optionalDependencies: + postcss: 8.5.6 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - preact-render-to-string + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + vitest@4.0.16(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@vitest/expect': 4.0.16 @@ -8140,3 +9197,5 @@ snapshots: grammex: 3.1.12 zod@4.3.5: {} + + zwitch@2.0.4: {} diff --git a/tools/docs/generate-command-reference.mjs b/tools/docs/generate-command-reference.mjs new file mode 100644 index 0000000..f5fe51e --- /dev/null +++ b/tools/docs/generate-command-reference.mjs @@ -0,0 +1,129 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import process from 'node:process'; +import prettier from 'prettier'; + +const workspaceRoot = process.cwd(); +const outputPath = path.join(workspaceRoot, 'docs', 'user', 'command-catalog.generated.md'); +const scopes = [ + { + id: 'general', + title: '장수 커맨드', + directory: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'general'), + registry: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'general', 'index.ts'), + registryName: 'GENERAL_TURN_COMMAND_KEYS', + }, + { + id: 'nation', + title: '국가 커맨드', + directory: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'nation'), + registry: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'nation', 'index.ts'), + registryName: 'NATION_TURN_COMMAND_KEYS', + }, +]; + +const readQuoted = (source, pattern) => source.match(pattern)?.[1] ?? null; + +const parseRegistry = (source, registryName) => { + const arrayBody = source.match(new RegExp(`export const ${registryName} = \\[([\\s\\S]*?)\\] as const;`))?.[1]; + if (!arrayBody) { + throw new Error(`명령 등록부를 찾을 수 없습니다: ${registryName}`); + } + return Array.from(arrayBody.matchAll(/['"]([^'"]+)['"]/g), (match) => match[1]); +}; + +const parseCommand = async (scope, key) => { + const sourcePath = path.join(scope.directory, `${key}.ts`); + const source = await fs.readFile(sourcePath, 'utf8'); + const actionName = + readQuoted(source, /const ACTION_NAME\s*=\s*['"]([^'"]+)['"]/) ?? + readQuoted(source, /public (?:override )?readonly name(?:\s*:\s*string)?\s*=\s*['"]([^'"]+)['"]/) ?? + readQuoted(source, /const DEFAULT_CONFIG[\s\S]*?\bname:\s*['"]([^'"]+)['"]/) ?? + readQuoted(source, /(?:super|createEventResearchCommand)\([\s\S]*?\{[\s\S]*?\bname:\s*['"]([^'"]+)['"]/); + const category = + readQuoted(source, /export const commandSpec[\s\S]*?\bcategory:\s*['"]([^'"]+)['"]/) ?? + (key.startsWith('event_') ? '특수' : null); + const reqArg = + source.match(/export const commandSpec[\s\S]*?\breqArg:\s*(true|false)/)?.[1] ?? + (key.startsWith('event_') ? 'false' : null); + const preReqTurn = source.match(/\bpreReqTurn:\s*(\d+)/)?.[1] ?? null; + + if (!actionName || !category || !reqArg) { + throw new Error( + `${path.relative(workspaceRoot, sourcePath)}에서 문서 메타데이터를 읽지 못했습니다: ` + + JSON.stringify({ actionName, category, reqArg }) + ); + } + + return { + key, + name: actionName, + category, + needsInput: reqArg === 'true', + preReqTurn, + }; +}; + +const escapeCell = (value) => String(value).replaceAll('|', '\\|').replaceAll('\n', ' '); + +const renderScope = (scope, commands) => { + const categoryOrder = [...new Set(commands.map((command) => command.category))]; + const sections = categoryOrder.map((category) => { + const rows = commands + .filter((command) => command.category === category) + .map((command) => { + const timing = command.preReqTurn ? `${Number(command.preReqTurn) + 1}턴 연속 실행` : '1턴'; + return `| \`${escapeCell(command.key)}\` | ${escapeCell(command.name)} | ${ + command.needsInput ? '필요' : '없음' + } | ${timing} |`; + }) + .join('\n'); + return `### ${category}\n\n| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |\n| --- | --- | --- | --- |\n${rows}`; + }); + + return `## ${scope.title}\n\n등록된 명령은 ${commands.length}개입니다.\n\n${sections.join('\n\n')}`; +}; + +const generated = []; +for (const scope of scopes) { + const registrySource = await fs.readFile(scope.registry, 'utf8'); + const keys = parseRegistry(registrySource, scope.registryName); + const commands = []; + for (const key of keys) { + commands.push(await parseCommand(scope, key)); + } + generated.push({ scope, commands }); +} + +const total = generated.reduce((sum, entry) => sum + entry.commands.length, 0); +const markdown = `--- +title: 커맨드 전체 목록 +outline: deep +--- + + + +# 커맨드 전체 목록 + +이 페이지는 현재 소스의 명령 등록부와 각 \`commandSpec\`에서 자동 생성됩니다. +총 ${total}개이며, profile의 \`resources/turn-commands/*.json\` 설정에 따라 실제 서버에서 일부가 제외될 수 +있습니다. “기본 실행 단위”는 정의에 고정된 선행 턴만 표시합니다. 자원, 신분, 도시, 외교, 시나리오 시점처럼 +실행 순간에 달라지는 조건은 [커맨드와 실행 시기](./commands-and-timing.md)를 확인해 주세요. + +${generated.map(({ scope, commands }) => renderScope(scope, commands)).join('\n\n')} + +## 생성 근거 + +- 등록 순서: \`packages/logic/src/actions/turn/general/index.ts\`, + \`packages/logic/src/actions/turn/nation/index.ts\` +- 표시명·분류·입력 여부·다중 턴: 각 명령 모듈의 \`commandSpec\`과 \`ActionDefinition\` +- 생성 명령: \`pnpm docs:generate\` +`; + +await fs.mkdir(path.dirname(outputPath), { recursive: true }); +await fs.writeFile(outputPath, await prettier.format(markdown, { parser: 'markdown' }), 'utf8'); +console.log( + `generated ${path.relative(workspaceRoot, outputPath)} (${generated[0].commands.length} general, ${ + generated[1].commands.length + } nation)` +);